roojs-ui.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787
788     /**
789      * Gets the number of cached records.
790      * <p>
791      * <em>If using paging, this may not be the total size of the dataset. If the data object
792      * used by the Reader contains the dataset size, then the getTotalCount() function returns
793      * the data set size</em>
794      */
795     getCount : function(){
796         return this.data.length || 0;
797     },
798
799     /**
800      * Gets the total number of records in the dataset as returned by the server.
801      * <p>
802      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
803      * the dataset size</em>
804      */
805     getTotalCount : function(){
806         return this.totalLength || 0;
807     },
808
809     /**
810      * Returns the sort state of the Store as an object with two properties:
811      * <pre><code>
812  field {String} The name of the field by which the Records are sorted
813  direction {String} The sort order, "ASC" or "DESC"
814      * </code></pre>
815      */
816     getSortState : function(){
817         return this.sortInfo;
818     },
819
820     // private
821     applySort : function(){
822         if(this.sortInfo && !this.remoteSort){
823             var s = this.sortInfo, f = s.field;
824             var st = this.fields.get(f).sortType;
825             var fn = function(r1, r2){
826                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
827                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
828             };
829             this.data.sort(s.direction, fn);
830             if(this.snapshot && this.snapshot != this.data){
831                 this.snapshot.sort(s.direction, fn);
832             }
833         }
834     },
835
836     /**
837      * Sets the default sort column and order to be used by the next load operation.
838      * @param {String} fieldName The name of the field to sort by.
839      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
840      */
841     setDefaultSort : function(field, dir){
842         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
843     },
844
845     /**
846      * Sort the Records.
847      * If remote sorting is used, the sort is performed on the server, and the cache is
848      * reloaded. If local sorting is used, the cache is sorted internally.
849      * @param {String} fieldName The name of the field to sort by.
850      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
851      */
852     sort : function(fieldName, dir){
853         var f = this.fields.get(fieldName);
854         if(!dir){
855             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
856             
857             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
858                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
859             }else{
860                 dir = f.sortDir;
861             }
862         }
863         this.sortToggle[f.name] = dir;
864         this.sortInfo = {field: f.name, direction: dir};
865         if(!this.remoteSort){
866             this.applySort();
867             this.fireEvent("datachanged", this);
868         }else{
869             this.load(this.lastOptions);
870         }
871     },
872
873     /**
874      * Calls the specified function for each of the Records in the cache.
875      * @param {Function} fn The function to call. The Record is passed as the first parameter.
876      * Returning <em>false</em> aborts and exits the iteration.
877      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
878      */
879     each : function(fn, scope){
880         this.data.each(fn, scope);
881     },
882
883     /**
884      * Gets all records modified since the last commit.  Modified records are persisted across load operations
885      * (e.g., during paging).
886      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
887      */
888     getModifiedRecords : function(){
889         return this.modified;
890     },
891
892     // private
893     createFilterFn : function(property, value, anyMatch){
894         if(!value.exec){ // not a regex
895             value = String(value);
896             if(value.length == 0){
897                 return false;
898             }
899             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
900         }
901         return function(r){
902             return value.test(r.data[property]);
903         };
904     },
905
906     /**
907      * Sums the value of <i>property</i> for each record between start and end and returns the result.
908      * @param {String} property A field on your records
909      * @param {Number} start The record index to start at (defaults to 0)
910      * @param {Number} end The last record index to include (defaults to length - 1)
911      * @return {Number} The sum
912      */
913     sum : function(property, start, end){
914         var rs = this.data.items, v = 0;
915         start = start || 0;
916         end = (end || end === 0) ? end : rs.length-1;
917
918         for(var i = start; i <= end; i++){
919             v += (rs[i].data[property] || 0);
920         }
921         return v;
922     },
923
924     /**
925      * Filter the records by a specified property.
926      * @param {String} field A field on your records
927      * @param {String/RegExp} value Either a string that the field
928      * should start with or a RegExp to test against the field
929      * @param {Boolean} anyMatch True to match any part not just the beginning
930      */
931     filter : function(property, value, anyMatch){
932         var fn = this.createFilterFn(property, value, anyMatch);
933         return fn ? this.filterBy(fn) : this.clearFilter();
934     },
935
936     /**
937      * Filter by a function. The specified function will be called with each
938      * record in this data source. If the function returns true the record is included,
939      * otherwise it is filtered.
940      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
941      * @param {Object} scope (optional) The scope of the function (defaults to this)
942      */
943     filterBy : function(fn, scope){
944         this.snapshot = this.snapshot || this.data;
945         this.data = this.queryBy(fn, scope||this);
946         this.fireEvent("datachanged", this);
947     },
948
949     /**
950      * Query the records by a specified property.
951      * @param {String} field A field on your records
952      * @param {String/RegExp} value Either a string that the field
953      * should start with or a RegExp to test against the field
954      * @param {Boolean} anyMatch True to match any part not just the beginning
955      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
956      */
957     query : function(property, value, anyMatch){
958         var fn = this.createFilterFn(property, value, anyMatch);
959         return fn ? this.queryBy(fn) : this.data.clone();
960     },
961
962     /**
963      * Query by a function. The specified function will be called with each
964      * record in this data source. If the function returns true the record is included
965      * in the results.
966      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
967      * @param {Object} scope (optional) The scope of the function (defaults to this)
968       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
969      **/
970     queryBy : function(fn, scope){
971         var data = this.snapshot || this.data;
972         return data.filterBy(fn, scope||this);
973     },
974
975     /**
976      * Collects unique values for a particular dataIndex from this store.
977      * @param {String} dataIndex The property to collect
978      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
979      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
980      * @return {Array} An array of the unique values
981      **/
982     collect : function(dataIndex, allowNull, bypassFilter){
983         var d = (bypassFilter === true && this.snapshot) ?
984                 this.snapshot.items : this.data.items;
985         var v, sv, r = [], l = {};
986         for(var i = 0, len = d.length; i < len; i++){
987             v = d[i].data[dataIndex];
988             sv = String(v);
989             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
990                 l[sv] = true;
991                 r[r.length] = v;
992             }
993         }
994         return r;
995     },
996
997     /**
998      * Revert to a view of the Record cache with no filtering applied.
999      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1000      */
1001     clearFilter : function(suppressEvent){
1002         if(this.snapshot && this.snapshot != this.data){
1003             this.data = this.snapshot;
1004             delete this.snapshot;
1005             if(suppressEvent !== true){
1006                 this.fireEvent("datachanged", this);
1007             }
1008         }
1009     },
1010
1011     // private
1012     afterEdit : function(record){
1013         if(this.modified.indexOf(record) == -1){
1014             this.modified.push(record);
1015         }
1016         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1017     },
1018     
1019     // private
1020     afterReject : function(record){
1021         this.modified.remove(record);
1022         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1023     },
1024
1025     // private
1026     afterCommit : function(record){
1027         this.modified.remove(record);
1028         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1029     },
1030
1031     /**
1032      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1033      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1034      */
1035     commitChanges : function(){
1036         var m = this.modified.slice(0);
1037         this.modified = [];
1038         for(var i = 0, len = m.length; i < len; i++){
1039             m[i].commit();
1040         }
1041     },
1042
1043     /**
1044      * Cancel outstanding changes on all changed records.
1045      */
1046     rejectChanges : function(){
1047         var m = this.modified.slice(0);
1048         this.modified = [];
1049         for(var i = 0, len = m.length; i < len; i++){
1050             m[i].reject();
1051         }
1052     },
1053
1054     onMetaChange : function(meta, rtype, o){
1055         this.recordType = rtype;
1056         this.fields = rtype.prototype.fields;
1057         delete this.snapshot;
1058         this.sortInfo = meta.sortInfo || this.sortInfo;
1059         this.modified = [];
1060         this.fireEvent('metachange', this, this.reader.meta);
1061     },
1062     
1063     moveIndex : function(data, type)
1064     {
1065         var index = this.indexOf(data);
1066         
1067         var newIndex = index + type;
1068         
1069         this.remove(data);
1070         
1071         this.insert(newIndex, data);
1072         
1073     }
1074 });/*
1075  * Based on:
1076  * Ext JS Library 1.1.1
1077  * Copyright(c) 2006-2007, Ext JS, LLC.
1078  *
1079  * Originally Released Under LGPL - original licence link has changed is not relivant.
1080  *
1081  * Fork - LGPL
1082  * <script type="text/javascript">
1083  */
1084
1085 /**
1086  * @class Roo.data.SimpleStore
1087  * @extends Roo.data.Store
1088  * Small helper class to make creating Stores from Array data easier.
1089  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1090  * @cfg {Array} fields An array of field definition objects, or field name strings.
1091  * @cfg {Object} an existing reader (eg. copied from another store)
1092  * @cfg {Array} data The multi-dimensional array of data
1093  * @constructor
1094  * @param {Object} config
1095  */
1096 Roo.data.SimpleStore = function(config)
1097 {
1098     Roo.data.SimpleStore.superclass.constructor.call(this, {
1099         isLocal : true,
1100         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1101                 id: config.id
1102             },
1103             Roo.data.Record.create(config.fields)
1104         ),
1105         proxy : new Roo.data.MemoryProxy(config.data)
1106     });
1107     this.load();
1108 };
1109 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1110  * Based on:
1111  * Ext JS Library 1.1.1
1112  * Copyright(c) 2006-2007, Ext JS, LLC.
1113  *
1114  * Originally Released Under LGPL - original licence link has changed is not relivant.
1115  *
1116  * Fork - LGPL
1117  * <script type="text/javascript">
1118  */
1119
1120 /**
1121 /**
1122  * @extends Roo.data.Store
1123  * @class Roo.data.JsonStore
1124  * Small helper class to make creating Stores for JSON data easier. <br/>
1125 <pre><code>
1126 var store = new Roo.data.JsonStore({
1127     url: 'get-images.php',
1128     root: 'images',
1129     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1130 });
1131 </code></pre>
1132  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1133  * JsonReader and HttpProxy (unless inline data is provided).</b>
1134  * @cfg {Array} fields An array of field definition objects, or field name strings.
1135  * @constructor
1136  * @param {Object} config
1137  */
1138 Roo.data.JsonStore = function(c){
1139     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1140         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1141         reader: new Roo.data.JsonReader(c, c.fields)
1142     }));
1143 };
1144 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1145  * Based on:
1146  * Ext JS Library 1.1.1
1147  * Copyright(c) 2006-2007, Ext JS, LLC.
1148  *
1149  * Originally Released Under LGPL - original licence link has changed is not relivant.
1150  *
1151  * Fork - LGPL
1152  * <script type="text/javascript">
1153  */
1154
1155  
1156 Roo.data.Field = function(config){
1157     if(typeof config == "string"){
1158         config = {name: config};
1159     }
1160     Roo.apply(this, config);
1161     
1162     if(!this.type){
1163         this.type = "auto";
1164     }
1165     
1166     var st = Roo.data.SortTypes;
1167     // named sortTypes are supported, here we look them up
1168     if(typeof this.sortType == "string"){
1169         this.sortType = st[this.sortType];
1170     }
1171     
1172     // set default sortType for strings and dates
1173     if(!this.sortType){
1174         switch(this.type){
1175             case "string":
1176                 this.sortType = st.asUCString;
1177                 break;
1178             case "date":
1179                 this.sortType = st.asDate;
1180                 break;
1181             default:
1182                 this.sortType = st.none;
1183         }
1184     }
1185
1186     // define once
1187     var stripRe = /[\$,%]/g;
1188
1189     // prebuilt conversion function for this field, instead of
1190     // switching every time we're reading a value
1191     if(!this.convert){
1192         var cv, dateFormat = this.dateFormat;
1193         switch(this.type){
1194             case "":
1195             case "auto":
1196             case undefined:
1197                 cv = function(v){ return v; };
1198                 break;
1199             case "string":
1200                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1201                 break;
1202             case "int":
1203                 cv = function(v){
1204                     return v !== undefined && v !== null && v !== '' ?
1205                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1206                     };
1207                 break;
1208             case "float":
1209                 cv = function(v){
1210                     return v !== undefined && v !== null && v !== '' ?
1211                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1212                     };
1213                 break;
1214             case "bool":
1215             case "boolean":
1216                 cv = function(v){ return v === true || v === "true" || v == 1; };
1217                 break;
1218             case "date":
1219                 cv = function(v){
1220                     if(!v){
1221                         return '';
1222                     }
1223                     if(v instanceof Date){
1224                         return v;
1225                     }
1226                     if(dateFormat){
1227                         if(dateFormat == "timestamp"){
1228                             return new Date(v*1000);
1229                         }
1230                         return Date.parseDate(v, dateFormat);
1231                     }
1232                     var parsed = Date.parse(v);
1233                     return parsed ? new Date(parsed) : null;
1234                 };
1235              break;
1236             
1237         }
1238         this.convert = cv;
1239     }
1240 };
1241
1242 Roo.data.Field.prototype = {
1243     dateFormat: null,
1244     defaultValue: "",
1245     mapping: null,
1246     sortType : null,
1247     sortDir : "ASC"
1248 };/*
1249  * Based on:
1250  * Ext JS Library 1.1.1
1251  * Copyright(c) 2006-2007, Ext JS, LLC.
1252  *
1253  * Originally Released Under LGPL - original licence link has changed is not relivant.
1254  *
1255  * Fork - LGPL
1256  * <script type="text/javascript">
1257  */
1258  
1259 // Base class for reading structured data from a data source.  This class is intended to be
1260 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1261
1262 /**
1263  * @class Roo.data.DataReader
1264  * Base class for reading structured data from a data source.  This class is intended to be
1265  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1266  */
1267
1268 Roo.data.DataReader = function(meta, recordType){
1269     
1270     this.meta = meta;
1271     
1272     this.recordType = recordType instanceof Array ? 
1273         Roo.data.Record.create(recordType) : recordType;
1274 };
1275
1276 Roo.data.DataReader.prototype = {
1277     
1278     
1279     readerType : 'Data',
1280      /**
1281      * Create an empty record
1282      * @param {Object} data (optional) - overlay some values
1283      * @return {Roo.data.Record} record created.
1284      */
1285     newRow :  function(d) {
1286         var da =  {};
1287         this.recordType.prototype.fields.each(function(c) {
1288             switch( c.type) {
1289                 case 'int' : da[c.name] = 0; break;
1290                 case 'date' : da[c.name] = new Date(); break;
1291                 case 'float' : da[c.name] = 0.0; break;
1292                 case 'boolean' : da[c.name] = false; break;
1293                 default : da[c.name] = ""; break;
1294             }
1295             
1296         });
1297         return new this.recordType(Roo.apply(da, d));
1298     }
1299     
1300     
1301 };/*
1302  * Based on:
1303  * Ext JS Library 1.1.1
1304  * Copyright(c) 2006-2007, Ext JS, LLC.
1305  *
1306  * Originally Released Under LGPL - original licence link has changed is not relivant.
1307  *
1308  * Fork - LGPL
1309  * <script type="text/javascript">
1310  */
1311
1312 /**
1313  * @class Roo.data.DataProxy
1314  * @extends Roo.data.Observable
1315  * This class is an abstract base class for implementations which provide retrieval of
1316  * unformatted data objects.<br>
1317  * <p>
1318  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1319  * (of the appropriate type which knows how to parse the data object) to provide a block of
1320  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1321  * <p>
1322  * Custom implementations must implement the load method as described in
1323  * {@link Roo.data.HttpProxy#load}.
1324  */
1325 Roo.data.DataProxy = function(){
1326     this.addEvents({
1327         /**
1328          * @event beforeload
1329          * Fires before a network request is made to retrieve a data object.
1330          * @param {Object} This DataProxy object.
1331          * @param {Object} params The params parameter to the load function.
1332          */
1333         beforeload : true,
1334         /**
1335          * @event load
1336          * Fires before the load method's callback is called.
1337          * @param {Object} This DataProxy object.
1338          * @param {Object} o The data object.
1339          * @param {Object} arg The callback argument object passed to the load function.
1340          */
1341         load : true,
1342         /**
1343          * @event loadexception
1344          * Fires if an Exception occurs during data retrieval.
1345          * @param {Object} This DataProxy object.
1346          * @param {Object} o The data object.
1347          * @param {Object} arg The callback argument object passed to the load function.
1348          * @param {Object} e The Exception.
1349          */
1350         loadexception : true
1351     });
1352     Roo.data.DataProxy.superclass.constructor.call(this);
1353 };
1354
1355 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1356
1357     /**
1358      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1359      */
1360 /*
1361  * Based on:
1362  * Ext JS Library 1.1.1
1363  * Copyright(c) 2006-2007, Ext JS, LLC.
1364  *
1365  * Originally Released Under LGPL - original licence link has changed is not relivant.
1366  *
1367  * Fork - LGPL
1368  * <script type="text/javascript">
1369  */
1370 /**
1371  * @class Roo.data.MemoryProxy
1372  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1373  * to the Reader when its load method is called.
1374  * @constructor
1375  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1376  */
1377 Roo.data.MemoryProxy = function(data){
1378     if (data.data) {
1379         data = data.data;
1380     }
1381     Roo.data.MemoryProxy.superclass.constructor.call(this);
1382     this.data = data;
1383 };
1384
1385 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1386     
1387     /**
1388      * Load data from the requested source (in this case an in-memory
1389      * data object passed to the constructor), read the data object into
1390      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1391      * process that block using the passed callback.
1392      * @param {Object} params This parameter is not used by the MemoryProxy class.
1393      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1394      * object into a block of Roo.data.Records.
1395      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1396      * The function must be passed <ul>
1397      * <li>The Record block object</li>
1398      * <li>The "arg" argument from the load function</li>
1399      * <li>A boolean success indicator</li>
1400      * </ul>
1401      * @param {Object} scope The scope in which to call the callback
1402      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1403      */
1404     load : function(params, reader, callback, scope, arg){
1405         params = params || {};
1406         var result;
1407         try {
1408             result = reader.readRecords(params.data ? params.data :this.data);
1409         }catch(e){
1410             this.fireEvent("loadexception", this, arg, null, e);
1411             callback.call(scope, null, arg, false);
1412             return;
1413         }
1414         callback.call(scope, result, arg, true);
1415     },
1416     
1417     // private
1418     update : function(params, records){
1419         
1420     }
1421 });/*
1422  * Based on:
1423  * Ext JS Library 1.1.1
1424  * Copyright(c) 2006-2007, Ext JS, LLC.
1425  *
1426  * Originally Released Under LGPL - original licence link has changed is not relivant.
1427  *
1428  * Fork - LGPL
1429  * <script type="text/javascript">
1430  */
1431 /**
1432  * @class Roo.data.HttpProxy
1433  * @extends Roo.data.DataProxy
1434  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1435  * configured to reference a certain URL.<br><br>
1436  * <p>
1437  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1438  * from which the running page was served.<br><br>
1439  * <p>
1440  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1441  * <p>
1442  * Be aware that to enable the browser to parse an XML document, the server must set
1443  * the Content-Type header in the HTTP response to "text/xml".
1444  * @constructor
1445  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1446  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1447  * will be used to make the request.
1448  */
1449 Roo.data.HttpProxy = function(conn){
1450     Roo.data.HttpProxy.superclass.constructor.call(this);
1451     // is conn a conn config or a real conn?
1452     this.conn = conn;
1453     this.useAjax = !conn || !conn.events;
1454   
1455 };
1456
1457 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1458     // thse are take from connection...
1459     
1460     /**
1461      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1462      */
1463     /**
1464      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1465      * extra parameters to each request made by this object. (defaults to undefined)
1466      */
1467     /**
1468      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1469      *  to each request made by this object. (defaults to undefined)
1470      */
1471     /**
1472      * @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)
1473      */
1474     /**
1475      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1476      */
1477      /**
1478      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1479      * @type Boolean
1480      */
1481   
1482
1483     /**
1484      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1485      * @type Boolean
1486      */
1487     /**
1488      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1489      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1490      * a finer-grained basis than the DataProxy events.
1491      */
1492     getConnection : function(){
1493         return this.useAjax ? Roo.Ajax : this.conn;
1494     },
1495
1496     /**
1497      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1498      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1499      * process that block using the passed callback.
1500      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1501      * for the request to the remote server.
1502      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1503      * object into a block of Roo.data.Records.
1504      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1505      * The function must be passed <ul>
1506      * <li>The Record block object</li>
1507      * <li>The "arg" argument from the load function</li>
1508      * <li>A boolean success indicator</li>
1509      * </ul>
1510      * @param {Object} scope The scope in which to call the callback
1511      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1512      */
1513     load : function(params, reader, callback, scope, arg){
1514         if(this.fireEvent("beforeload", this, params) !== false){
1515             var  o = {
1516                 params : params || {},
1517                 request: {
1518                     callback : callback,
1519                     scope : scope,
1520                     arg : arg
1521                 },
1522                 reader: reader,
1523                 callback : this.loadResponse,
1524                 scope: this
1525             };
1526             if(this.useAjax){
1527                 Roo.applyIf(o, this.conn);
1528                 if(this.activeRequest){
1529                     Roo.Ajax.abort(this.activeRequest);
1530                 }
1531                 this.activeRequest = Roo.Ajax.request(o);
1532             }else{
1533                 this.conn.request(o);
1534             }
1535         }else{
1536             callback.call(scope||this, null, arg, false);
1537         }
1538     },
1539
1540     // private
1541     loadResponse : function(o, success, response){
1542         delete this.activeRequest;
1543         if(!success){
1544             this.fireEvent("loadexception", this, o, response);
1545             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1546             return;
1547         }
1548         var result;
1549         try {
1550             result = o.reader.read(response);
1551         }catch(e){
1552             this.fireEvent("loadexception", this, o, response, e);
1553             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1554             return;
1555         }
1556         
1557         this.fireEvent("load", this, o, o.request.arg);
1558         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1559     },
1560
1561     // private
1562     update : function(dataSet){
1563
1564     },
1565
1566     // private
1567     updateResponse : function(dataSet){
1568
1569     }
1570 });/*
1571  * Based on:
1572  * Ext JS Library 1.1.1
1573  * Copyright(c) 2006-2007, Ext JS, LLC.
1574  *
1575  * Originally Released Under LGPL - original licence link has changed is not relivant.
1576  *
1577  * Fork - LGPL
1578  * <script type="text/javascript">
1579  */
1580
1581 /**
1582  * @class Roo.data.ScriptTagProxy
1583  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1584  * other than the originating domain of the running page.<br><br>
1585  * <p>
1586  * <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
1587  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1588  * <p>
1589  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1590  * source code that is used as the source inside a &lt;script> tag.<br><br>
1591  * <p>
1592  * In order for the browser to process the returned data, the server must wrap the data object
1593  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1594  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1595  * depending on whether the callback name was passed:
1596  * <p>
1597  * <pre><code>
1598 boolean scriptTag = false;
1599 String cb = request.getParameter("callback");
1600 if (cb != null) {
1601     scriptTag = true;
1602     response.setContentType("text/javascript");
1603 } else {
1604     response.setContentType("application/x-json");
1605 }
1606 Writer out = response.getWriter();
1607 if (scriptTag) {
1608     out.write(cb + "(");
1609 }
1610 out.print(dataBlock.toJsonString());
1611 if (scriptTag) {
1612     out.write(");");
1613 }
1614 </pre></code>
1615  *
1616  * @constructor
1617  * @param {Object} config A configuration object.
1618  */
1619 Roo.data.ScriptTagProxy = function(config){
1620     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1621     Roo.apply(this, config);
1622     this.head = document.getElementsByTagName("head")[0];
1623 };
1624
1625 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1626
1627 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1628     /**
1629      * @cfg {String} url The URL from which to request the data object.
1630      */
1631     /**
1632      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1633      */
1634     timeout : 30000,
1635     /**
1636      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1637      * the server the name of the callback function set up by the load call to process the returned data object.
1638      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1639      * javascript output which calls this named function passing the data object as its only parameter.
1640      */
1641     callbackParam : "callback",
1642     /**
1643      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1644      * name to the request.
1645      */
1646     nocache : true,
1647
1648     /**
1649      * Load data from the configured URL, read the data object into
1650      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1651      * process that block using the passed callback.
1652      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1653      * for the request to the remote server.
1654      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1655      * object into a block of Roo.data.Records.
1656      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1657      * The function must be passed <ul>
1658      * <li>The Record block object</li>
1659      * <li>The "arg" argument from the load function</li>
1660      * <li>A boolean success indicator</li>
1661      * </ul>
1662      * @param {Object} scope The scope in which to call the callback
1663      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1664      */
1665     load : function(params, reader, callback, scope, arg){
1666         if(this.fireEvent("beforeload", this, params) !== false){
1667
1668             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1669
1670             var url = this.url;
1671             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1672             if(this.nocache){
1673                 url += "&_dc=" + (new Date().getTime());
1674             }
1675             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1676             var trans = {
1677                 id : transId,
1678                 cb : "stcCallback"+transId,
1679                 scriptId : "stcScript"+transId,
1680                 params : params,
1681                 arg : arg,
1682                 url : url,
1683                 callback : callback,
1684                 scope : scope,
1685                 reader : reader
1686             };
1687             var conn = this;
1688
1689             window[trans.cb] = function(o){
1690                 conn.handleResponse(o, trans);
1691             };
1692
1693             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1694
1695             if(this.autoAbort !== false){
1696                 this.abort();
1697             }
1698
1699             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1700
1701             var script = document.createElement("script");
1702             script.setAttribute("src", url);
1703             script.setAttribute("type", "text/javascript");
1704             script.setAttribute("id", trans.scriptId);
1705             this.head.appendChild(script);
1706
1707             this.trans = trans;
1708         }else{
1709             callback.call(scope||this, null, arg, false);
1710         }
1711     },
1712
1713     // private
1714     isLoading : function(){
1715         return this.trans ? true : false;
1716     },
1717
1718     /**
1719      * Abort the current server request.
1720      */
1721     abort : function(){
1722         if(this.isLoading()){
1723             this.destroyTrans(this.trans);
1724         }
1725     },
1726
1727     // private
1728     destroyTrans : function(trans, isLoaded){
1729         this.head.removeChild(document.getElementById(trans.scriptId));
1730         clearTimeout(trans.timeoutId);
1731         if(isLoaded){
1732             window[trans.cb] = undefined;
1733             try{
1734                 delete window[trans.cb];
1735             }catch(e){}
1736         }else{
1737             // if hasn't been loaded, wait for load to remove it to prevent script error
1738             window[trans.cb] = function(){
1739                 window[trans.cb] = undefined;
1740                 try{
1741                     delete window[trans.cb];
1742                 }catch(e){}
1743             };
1744         }
1745     },
1746
1747     // private
1748     handleResponse : function(o, trans){
1749         this.trans = false;
1750         this.destroyTrans(trans, true);
1751         var result;
1752         try {
1753             result = trans.reader.readRecords(o);
1754         }catch(e){
1755             this.fireEvent("loadexception", this, o, trans.arg, e);
1756             trans.callback.call(trans.scope||window, null, trans.arg, false);
1757             return;
1758         }
1759         this.fireEvent("load", this, o, trans.arg);
1760         trans.callback.call(trans.scope||window, result, trans.arg, true);
1761     },
1762
1763     // private
1764     handleFailure : function(trans){
1765         this.trans = false;
1766         this.destroyTrans(trans, false);
1767         this.fireEvent("loadexception", this, null, trans.arg);
1768         trans.callback.call(trans.scope||window, null, trans.arg, false);
1769     }
1770 });/*
1771  * Based on:
1772  * Ext JS Library 1.1.1
1773  * Copyright(c) 2006-2007, Ext JS, LLC.
1774  *
1775  * Originally Released Under LGPL - original licence link has changed is not relivant.
1776  *
1777  * Fork - LGPL
1778  * <script type="text/javascript">
1779  */
1780
1781 /**
1782  * @class Roo.data.JsonReader
1783  * @extends Roo.data.DataReader
1784  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1785  * based on mappings in a provided Roo.data.Record constructor.
1786  * 
1787  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1788  * in the reply previously. 
1789  * 
1790  * <p>
1791  * Example code:
1792  * <pre><code>
1793 var RecordDef = Roo.data.Record.create([
1794     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1795     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1796 ]);
1797 var myReader = new Roo.data.JsonReader({
1798     totalProperty: "results",    // The property which contains the total dataset size (optional)
1799     root: "rows",                // The property which contains an Array of row objects
1800     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1801 }, RecordDef);
1802 </code></pre>
1803  * <p>
1804  * This would consume a JSON file like this:
1805  * <pre><code>
1806 { 'results': 2, 'rows': [
1807     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1808     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1809 }
1810 </code></pre>
1811  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1812  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1813  * paged from the remote server.
1814  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1815  * @cfg {String} root name of the property which contains the Array of row objects.
1816  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1817  * @cfg {Array} fields Array of field definition objects
1818  * @constructor
1819  * Create a new JsonReader
1820  * @param {Object} meta Metadata configuration options
1821  * @param {Object} recordType Either an Array of field definition objects,
1822  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1823  */
1824 Roo.data.JsonReader = function(meta, recordType){
1825     
1826     meta = meta || {};
1827     // set some defaults:
1828     Roo.applyIf(meta, {
1829         totalProperty: 'total',
1830         successProperty : 'success',
1831         root : 'data',
1832         id : 'id'
1833     });
1834     
1835     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1836 };
1837 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1838     
1839     readerType : 'Json',
1840     
1841     /**
1842      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1843      * Used by Store query builder to append _requestMeta to params.
1844      * 
1845      */
1846     metaFromRemote : false,
1847     /**
1848      * This method is only used by a DataProxy which has retrieved data from a remote server.
1849      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1850      * @return {Object} data A data block which is used by an Roo.data.Store object as
1851      * a cache of Roo.data.Records.
1852      */
1853     read : function(response){
1854         var json = response.responseText;
1855        
1856         var o = /* eval:var:o */ eval("("+json+")");
1857         if(!o) {
1858             throw {message: "JsonReader.read: Json object not found"};
1859         }
1860         
1861         if(o.metaData){
1862             
1863             delete this.ef;
1864             this.metaFromRemote = true;
1865             this.meta = o.metaData;
1866             this.recordType = Roo.data.Record.create(o.metaData.fields);
1867             this.onMetaChange(this.meta, this.recordType, o);
1868         }
1869         return this.readRecords(o);
1870     },
1871
1872     // private function a store will implement
1873     onMetaChange : function(meta, recordType, o){
1874
1875     },
1876
1877     /**
1878          * @ignore
1879          */
1880     simpleAccess: function(obj, subsc) {
1881         return obj[subsc];
1882     },
1883
1884         /**
1885          * @ignore
1886          */
1887     getJsonAccessor: function(){
1888         var re = /[\[\.]/;
1889         return function(expr) {
1890             try {
1891                 return(re.test(expr))
1892                     ? new Function("obj", "return obj." + expr)
1893                     : function(obj){
1894                         return obj[expr];
1895                     };
1896             } catch(e){}
1897             return Roo.emptyFn;
1898         };
1899     }(),
1900
1901     /**
1902      * Create a data block containing Roo.data.Records from an XML document.
1903      * @param {Object} o An object which contains an Array of row objects in the property specified
1904      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1905      * which contains the total size of the dataset.
1906      * @return {Object} data A data block which is used by an Roo.data.Store object as
1907      * a cache of Roo.data.Records.
1908      */
1909     readRecords : function(o){
1910         /**
1911          * After any data loads, the raw JSON data is available for further custom processing.
1912          * @type Object
1913          */
1914         this.o = o;
1915         var s = this.meta, Record = this.recordType,
1916             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1917
1918 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1919         if (!this.ef) {
1920             if(s.totalProperty) {
1921                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1922                 }
1923                 if(s.successProperty) {
1924                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1925                 }
1926                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1927                 if (s.id) {
1928                         var g = this.getJsonAccessor(s.id);
1929                         this.getId = function(rec) {
1930                                 var r = g(rec);  
1931                                 return (r === undefined || r === "") ? null : r;
1932                         };
1933                 } else {
1934                         this.getId = function(){return null;};
1935                 }
1936             this.ef = [];
1937             for(var jj = 0; jj < fl; jj++){
1938                 f = fi[jj];
1939                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1940                 this.ef[jj] = this.getJsonAccessor(map);
1941             }
1942         }
1943
1944         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1945         if(s.totalProperty){
1946             var vt = parseInt(this.getTotal(o), 10);
1947             if(!isNaN(vt)){
1948                 totalRecords = vt;
1949             }
1950         }
1951         if(s.successProperty){
1952             var vs = this.getSuccess(o);
1953             if(vs === false || vs === 'false'){
1954                 success = false;
1955             }
1956         }
1957         var records = [];
1958         for(var i = 0; i < c; i++){
1959                 var n = root[i];
1960             var values = {};
1961             var id = this.getId(n);
1962             for(var j = 0; j < fl; j++){
1963                 f = fi[j];
1964             var v = this.ef[j](n);
1965             if (!f.convert) {
1966                 Roo.log('missing convert for ' + f.name);
1967                 Roo.log(f);
1968                 continue;
1969             }
1970             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1971             }
1972             var record = new Record(values, id);
1973             record.json = n;
1974             records[i] = record;
1975         }
1976         return {
1977             raw : o,
1978             success : success,
1979             records : records,
1980             totalRecords : totalRecords
1981         };
1982     }
1983 });/*
1984  * Based on:
1985  * Ext JS Library 1.1.1
1986  * Copyright(c) 2006-2007, Ext JS, LLC.
1987  *
1988  * Originally Released Under LGPL - original licence link has changed is not relivant.
1989  *
1990  * Fork - LGPL
1991  * <script type="text/javascript">
1992  */
1993
1994 /**
1995  * @class Roo.data.XmlReader
1996  * @extends Roo.data.DataReader
1997  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
1998  * based on mappings in a provided Roo.data.Record constructor.<br><br>
1999  * <p>
2000  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2001  * header in the HTTP response must be set to "text/xml".</em>
2002  * <p>
2003  * Example code:
2004  * <pre><code>
2005 var RecordDef = Roo.data.Record.create([
2006    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2007    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2008 ]);
2009 var myReader = new Roo.data.XmlReader({
2010    totalRecords: "results", // The element which contains the total dataset size (optional)
2011    record: "row",           // The repeated element which contains row information
2012    id: "id"                 // The element within the row that provides an ID for the record (optional)
2013 }, RecordDef);
2014 </code></pre>
2015  * <p>
2016  * This would consume an XML file like this:
2017  * <pre><code>
2018 &lt;?xml?>
2019 &lt;dataset>
2020  &lt;results>2&lt;/results>
2021  &lt;row>
2022    &lt;id>1&lt;/id>
2023    &lt;name>Bill&lt;/name>
2024    &lt;occupation>Gardener&lt;/occupation>
2025  &lt;/row>
2026  &lt;row>
2027    &lt;id>2&lt;/id>
2028    &lt;name>Ben&lt;/name>
2029    &lt;occupation>Horticulturalist&lt;/occupation>
2030  &lt;/row>
2031 &lt;/dataset>
2032 </code></pre>
2033  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2034  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2035  * paged from the remote server.
2036  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2037  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2038  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2039  * a record identifier value.
2040  * @constructor
2041  * Create a new XmlReader
2042  * @param {Object} meta Metadata configuration options
2043  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2044  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2045  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2046  */
2047 Roo.data.XmlReader = function(meta, recordType){
2048     meta = meta || {};
2049     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2050 };
2051 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2052     
2053     readerType : 'Xml',
2054     
2055     /**
2056      * This method is only used by a DataProxy which has retrieved data from a remote server.
2057          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2058          * to contain a method called 'responseXML' that returns an XML document object.
2059      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2060      * a cache of Roo.data.Records.
2061      */
2062     read : function(response){
2063         var doc = response.responseXML;
2064         if(!doc) {
2065             throw {message: "XmlReader.read: XML Document not available"};
2066         }
2067         return this.readRecords(doc);
2068     },
2069
2070     /**
2071      * Create a data block containing Roo.data.Records from an XML document.
2072          * @param {Object} doc A parsed XML document.
2073      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2074      * a cache of Roo.data.Records.
2075      */
2076     readRecords : function(doc){
2077         /**
2078          * After any data loads/reads, the raw XML Document is available for further custom processing.
2079          * @type XMLDocument
2080          */
2081         this.xmlData = doc;
2082         var root = doc.documentElement || doc;
2083         var q = Roo.DomQuery;
2084         var recordType = this.recordType, fields = recordType.prototype.fields;
2085         var sid = this.meta.id;
2086         var totalRecords = 0, success = true;
2087         if(this.meta.totalRecords){
2088             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2089         }
2090         
2091         if(this.meta.success){
2092             var sv = q.selectValue(this.meta.success, root, true);
2093             success = sv !== false && sv !== 'false';
2094         }
2095         var records = [];
2096         var ns = q.select(this.meta.record, root);
2097         for(var i = 0, len = ns.length; i < len; i++) {
2098                 var n = ns[i];
2099                 var values = {};
2100                 var id = sid ? q.selectValue(sid, n) : undefined;
2101                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2102                     var f = fields.items[j];
2103                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2104                     v = f.convert(v);
2105                     values[f.name] = v;
2106                 }
2107                 var record = new recordType(values, id);
2108                 record.node = n;
2109                 records[records.length] = record;
2110             }
2111
2112             return {
2113                 success : success,
2114                 records : records,
2115                 totalRecords : totalRecords || records.length
2116             };
2117     }
2118 });/*
2119  * Based on:
2120  * Ext JS Library 1.1.1
2121  * Copyright(c) 2006-2007, Ext JS, LLC.
2122  *
2123  * Originally Released Under LGPL - original licence link has changed is not relivant.
2124  *
2125  * Fork - LGPL
2126  * <script type="text/javascript">
2127  */
2128
2129 /**
2130  * @class Roo.data.ArrayReader
2131  * @extends Roo.data.DataReader
2132  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2133  * Each element of that Array represents a row of data fields. The
2134  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2135  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2136  * <p>
2137  * Example code:.
2138  * <pre><code>
2139 var RecordDef = Roo.data.Record.create([
2140     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2141     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2142 ]);
2143 var myReader = new Roo.data.ArrayReader({
2144     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2145 }, RecordDef);
2146 </code></pre>
2147  * <p>
2148  * This would consume an Array like this:
2149  * <pre><code>
2150 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2151   </code></pre>
2152  
2153  * @constructor
2154  * Create a new JsonReader
2155  * @param {Object} meta Metadata configuration options.
2156  * @param {Object|Array} recordType Either an Array of field definition objects
2157  * 
2158  * @cfg {Array} fields Array of field definition objects
2159  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2160  * as specified to {@link Roo.data.Record#create},
2161  * or an {@link Roo.data.Record} object
2162  *
2163  * 
2164  * created using {@link Roo.data.Record#create}.
2165  */
2166 Roo.data.ArrayReader = function(meta, recordType)
2167 {    
2168     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2169 };
2170
2171 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2172     
2173     readerType : 'Array',
2174     /**
2175      * Create a data block containing Roo.data.Records from an XML document.
2176      * @param {Object} o An Array of row objects which represents the dataset.
2177      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2178      * a cache of Roo.data.Records.
2179      */
2180     readRecords : function(o)
2181     {
2182         var sid = this.meta ? this.meta.id : null;
2183         var recordType = this.recordType, fields = recordType.prototype.fields;
2184         var records = [];
2185         var root = o;
2186         for(var i = 0; i < root.length; i++){
2187                 var n = root[i];
2188             var values = {};
2189             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2190             for(var j = 0, jlen = fields.length; j < jlen; j++){
2191                 var f = fields.items[j];
2192                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2193                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2194                 v = f.convert(v);
2195                 values[f.name] = v;
2196             }
2197             var record = new recordType(values, id);
2198             record.json = n;
2199             records[records.length] = record;
2200         }
2201         return {
2202             records : records,
2203             totalRecords : records.length
2204         };
2205     }
2206 });/*
2207  * Based on:
2208  * Ext JS Library 1.1.1
2209  * Copyright(c) 2006-2007, Ext JS, LLC.
2210  *
2211  * Originally Released Under LGPL - original licence link has changed is not relivant.
2212  *
2213  * Fork - LGPL
2214  * <script type="text/javascript">
2215  */
2216
2217
2218 /**
2219  * @class Roo.data.Tree
2220  * @extends Roo.util.Observable
2221  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2222  * in the tree have most standard DOM functionality.
2223  * @constructor
2224  * @param {Node} root (optional) The root node
2225  */
2226 Roo.data.Tree = function(root){
2227    this.nodeHash = {};
2228    /**
2229     * The root node for this tree
2230     * @type Node
2231     */
2232    this.root = null;
2233    if(root){
2234        this.setRootNode(root);
2235    }
2236    this.addEvents({
2237        /**
2238         * @event append
2239         * Fires when a new child node is appended to a node in this tree.
2240         * @param {Tree} tree The owner tree
2241         * @param {Node} parent The parent node
2242         * @param {Node} node The newly appended node
2243         * @param {Number} index The index of the newly appended node
2244         */
2245        "append" : true,
2246        /**
2247         * @event remove
2248         * Fires when a child node is removed from a node in this tree.
2249         * @param {Tree} tree The owner tree
2250         * @param {Node} parent The parent node
2251         * @param {Node} node The child node removed
2252         */
2253        "remove" : true,
2254        /**
2255         * @event move
2256         * Fires when a node is moved to a new location in the tree
2257         * @param {Tree} tree The owner tree
2258         * @param {Node} node The node moved
2259         * @param {Node} oldParent The old parent of this node
2260         * @param {Node} newParent The new parent of this node
2261         * @param {Number} index The index it was moved to
2262         */
2263        "move" : true,
2264        /**
2265         * @event insert
2266         * Fires when a new child node is inserted in a node in this tree.
2267         * @param {Tree} tree The owner tree
2268         * @param {Node} parent The parent node
2269         * @param {Node} node The child node inserted
2270         * @param {Node} refNode The child node the node was inserted before
2271         */
2272        "insert" : true,
2273        /**
2274         * @event beforeappend
2275         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2276         * @param {Tree} tree The owner tree
2277         * @param {Node} parent The parent node
2278         * @param {Node} node The child node to be appended
2279         */
2280        "beforeappend" : true,
2281        /**
2282         * @event beforeremove
2283         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2284         * @param {Tree} tree The owner tree
2285         * @param {Node} parent The parent node
2286         * @param {Node} node The child node to be removed
2287         */
2288        "beforeremove" : true,
2289        /**
2290         * @event beforemove
2291         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2292         * @param {Tree} tree The owner tree
2293         * @param {Node} node The node being moved
2294         * @param {Node} oldParent The parent of the node
2295         * @param {Node} newParent The new parent the node is moving to
2296         * @param {Number} index The index it is being moved to
2297         */
2298        "beforemove" : true,
2299        /**
2300         * @event beforeinsert
2301         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2302         * @param {Tree} tree The owner tree
2303         * @param {Node} parent The parent node
2304         * @param {Node} node The child node to be inserted
2305         * @param {Node} refNode The child node the node is being inserted before
2306         */
2307        "beforeinsert" : true
2308    });
2309
2310     Roo.data.Tree.superclass.constructor.call(this);
2311 };
2312
2313 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2314     pathSeparator: "/",
2315
2316     proxyNodeEvent : function(){
2317         return this.fireEvent.apply(this, arguments);
2318     },
2319
2320     /**
2321      * Returns the root node for this tree.
2322      * @return {Node}
2323      */
2324     getRootNode : function(){
2325         return this.root;
2326     },
2327
2328     /**
2329      * Sets the root node for this tree.
2330      * @param {Node} node
2331      * @return {Node}
2332      */
2333     setRootNode : function(node){
2334         this.root = node;
2335         node.ownerTree = this;
2336         node.isRoot = true;
2337         this.registerNode(node);
2338         return node;
2339     },
2340
2341     /**
2342      * Gets a node in this tree by its id.
2343      * @param {String} id
2344      * @return {Node}
2345      */
2346     getNodeById : function(id){
2347         return this.nodeHash[id];
2348     },
2349
2350     registerNode : function(node){
2351         this.nodeHash[node.id] = node;
2352     },
2353
2354     unregisterNode : function(node){
2355         delete this.nodeHash[node.id];
2356     },
2357
2358     toString : function(){
2359         return "[Tree"+(this.id?" "+this.id:"")+"]";
2360     }
2361 });
2362
2363 /**
2364  * @class Roo.data.Node
2365  * @extends Roo.util.Observable
2366  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2367  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2368  * @constructor
2369  * @param {Object} attributes The attributes/config for the node
2370  */
2371 Roo.data.Node = function(attributes){
2372     /**
2373      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2374      * @type {Object}
2375      */
2376     this.attributes = attributes || {};
2377     this.leaf = this.attributes.leaf;
2378     /**
2379      * The node id. @type String
2380      */
2381     this.id = this.attributes.id;
2382     if(!this.id){
2383         this.id = Roo.id(null, "ynode-");
2384         this.attributes.id = this.id;
2385     }
2386      
2387     
2388     /**
2389      * All child nodes of this node. @type Array
2390      */
2391     this.childNodes = [];
2392     if(!this.childNodes.indexOf){ // indexOf is a must
2393         this.childNodes.indexOf = function(o){
2394             for(var i = 0, len = this.length; i < len; i++){
2395                 if(this[i] == o) {
2396                     return i;
2397                 }
2398             }
2399             return -1;
2400         };
2401     }
2402     /**
2403      * The parent node for this node. @type Node
2404      */
2405     this.parentNode = null;
2406     /**
2407      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2408      */
2409     this.firstChild = null;
2410     /**
2411      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2412      */
2413     this.lastChild = null;
2414     /**
2415      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2416      */
2417     this.previousSibling = null;
2418     /**
2419      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2420      */
2421     this.nextSibling = null;
2422
2423     this.addEvents({
2424        /**
2425         * @event append
2426         * Fires when a new child node is appended
2427         * @param {Tree} tree The owner tree
2428         * @param {Node} this This node
2429         * @param {Node} node The newly appended node
2430         * @param {Number} index The index of the newly appended node
2431         */
2432        "append" : true,
2433        /**
2434         * @event remove
2435         * Fires when a child node is removed
2436         * @param {Tree} tree The owner tree
2437         * @param {Node} this This node
2438         * @param {Node} node The removed node
2439         */
2440        "remove" : true,
2441        /**
2442         * @event move
2443         * Fires when this node is moved to a new location in the tree
2444         * @param {Tree} tree The owner tree
2445         * @param {Node} this This node
2446         * @param {Node} oldParent The old parent of this node
2447         * @param {Node} newParent The new parent of this node
2448         * @param {Number} index The index it was moved to
2449         */
2450        "move" : true,
2451        /**
2452         * @event insert
2453         * Fires when a new child node is inserted.
2454         * @param {Tree} tree The owner tree
2455         * @param {Node} this This node
2456         * @param {Node} node The child node inserted
2457         * @param {Node} refNode The child node the node was inserted before
2458         */
2459        "insert" : true,
2460        /**
2461         * @event beforeappend
2462         * Fires before a new child is appended, return false to cancel the append.
2463         * @param {Tree} tree The owner tree
2464         * @param {Node} this This node
2465         * @param {Node} node The child node to be appended
2466         */
2467        "beforeappend" : true,
2468        /**
2469         * @event beforeremove
2470         * Fires before a child is removed, return false to cancel the remove.
2471         * @param {Tree} tree The owner tree
2472         * @param {Node} this This node
2473         * @param {Node} node The child node to be removed
2474         */
2475        "beforeremove" : true,
2476        /**
2477         * @event beforemove
2478         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2479         * @param {Tree} tree The owner tree
2480         * @param {Node} this This node
2481         * @param {Node} oldParent The parent of this node
2482         * @param {Node} newParent The new parent this node is moving to
2483         * @param {Number} index The index it is being moved to
2484         */
2485        "beforemove" : true,
2486        /**
2487         * @event beforeinsert
2488         * Fires before a new child is inserted, return false to cancel the insert.
2489         * @param {Tree} tree The owner tree
2490         * @param {Node} this This node
2491         * @param {Node} node The child node to be inserted
2492         * @param {Node} refNode The child node the node is being inserted before
2493         */
2494        "beforeinsert" : true
2495    });
2496     this.listeners = this.attributes.listeners;
2497     Roo.data.Node.superclass.constructor.call(this);
2498 };
2499
2500 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2501     fireEvent : function(evtName){
2502         // first do standard event for this node
2503         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2504             return false;
2505         }
2506         // then bubble it up to the tree if the event wasn't cancelled
2507         var ot = this.getOwnerTree();
2508         if(ot){
2509             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2510                 return false;
2511             }
2512         }
2513         return true;
2514     },
2515
2516     /**
2517      * Returns true if this node is a leaf
2518      * @return {Boolean}
2519      */
2520     isLeaf : function(){
2521         return this.leaf === true;
2522     },
2523
2524     // private
2525     setFirstChild : function(node){
2526         this.firstChild = node;
2527     },
2528
2529     //private
2530     setLastChild : function(node){
2531         this.lastChild = node;
2532     },
2533
2534
2535     /**
2536      * Returns true if this node is the last child of its parent
2537      * @return {Boolean}
2538      */
2539     isLast : function(){
2540        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2541     },
2542
2543     /**
2544      * Returns true if this node is the first child of its parent
2545      * @return {Boolean}
2546      */
2547     isFirst : function(){
2548        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2549     },
2550
2551     hasChildNodes : function(){
2552         return !this.isLeaf() && this.childNodes.length > 0;
2553     },
2554
2555     /**
2556      * Insert node(s) as the last child node of this node.
2557      * @param {Node/Array} node The node or Array of nodes to append
2558      * @return {Node} The appended node if single append, or null if an array was passed
2559      */
2560     appendChild : function(node){
2561         var multi = false;
2562         if(node instanceof Array){
2563             multi = node;
2564         }else if(arguments.length > 1){
2565             multi = arguments;
2566         }
2567         
2568         // if passed an array or multiple args do them one by one
2569         if(multi){
2570             for(var i = 0, len = multi.length; i < len; i++) {
2571                 this.appendChild(multi[i]);
2572             }
2573         }else{
2574             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2575                 return false;
2576             }
2577             var index = this.childNodes.length;
2578             var oldParent = node.parentNode;
2579             // it's a move, make sure we move it cleanly
2580             if(oldParent){
2581                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2582                     return false;
2583                 }
2584                 oldParent.removeChild(node);
2585             }
2586             
2587             index = this.childNodes.length;
2588             if(index == 0){
2589                 this.setFirstChild(node);
2590             }
2591             this.childNodes.push(node);
2592             node.parentNode = this;
2593             var ps = this.childNodes[index-1];
2594             if(ps){
2595                 node.previousSibling = ps;
2596                 ps.nextSibling = node;
2597             }else{
2598                 node.previousSibling = null;
2599             }
2600             node.nextSibling = null;
2601             this.setLastChild(node);
2602             node.setOwnerTree(this.getOwnerTree());
2603             this.fireEvent("append", this.ownerTree, this, node, index);
2604             if(this.ownerTree) {
2605                 this.ownerTree.fireEvent("appendnode", this, node, index);
2606             }
2607             if(oldParent){
2608                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2609             }
2610             return node;
2611         }
2612     },
2613
2614     /**
2615      * Removes a child node from this node.
2616      * @param {Node} node The node to remove
2617      * @return {Node} The removed node
2618      */
2619     removeChild : function(node){
2620         var index = this.childNodes.indexOf(node);
2621         if(index == -1){
2622             return false;
2623         }
2624         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2625             return false;
2626         }
2627
2628         // remove it from childNodes collection
2629         this.childNodes.splice(index, 1);
2630
2631         // update siblings
2632         if(node.previousSibling){
2633             node.previousSibling.nextSibling = node.nextSibling;
2634         }
2635         if(node.nextSibling){
2636             node.nextSibling.previousSibling = node.previousSibling;
2637         }
2638
2639         // update child refs
2640         if(this.firstChild == node){
2641             this.setFirstChild(node.nextSibling);
2642         }
2643         if(this.lastChild == node){
2644             this.setLastChild(node.previousSibling);
2645         }
2646
2647         node.setOwnerTree(null);
2648         // clear any references from the node
2649         node.parentNode = null;
2650         node.previousSibling = null;
2651         node.nextSibling = null;
2652         this.fireEvent("remove", this.ownerTree, this, node);
2653         return node;
2654     },
2655
2656     /**
2657      * Inserts the first node before the second node in this nodes childNodes collection.
2658      * @param {Node} node The node to insert
2659      * @param {Node} refNode The node to insert before (if null the node is appended)
2660      * @return {Node} The inserted node
2661      */
2662     insertBefore : function(node, refNode){
2663         if(!refNode){ // like standard Dom, refNode can be null for append
2664             return this.appendChild(node);
2665         }
2666         // nothing to do
2667         if(node == refNode){
2668             return false;
2669         }
2670
2671         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2672             return false;
2673         }
2674         var index = this.childNodes.indexOf(refNode);
2675         var oldParent = node.parentNode;
2676         var refIndex = index;
2677
2678         // when moving internally, indexes will change after remove
2679         if(oldParent == this && this.childNodes.indexOf(node) < index){
2680             refIndex--;
2681         }
2682
2683         // it's a move, make sure we move it cleanly
2684         if(oldParent){
2685             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2686                 return false;
2687             }
2688             oldParent.removeChild(node);
2689         }
2690         if(refIndex == 0){
2691             this.setFirstChild(node);
2692         }
2693         this.childNodes.splice(refIndex, 0, node);
2694         node.parentNode = this;
2695         var ps = this.childNodes[refIndex-1];
2696         if(ps){
2697             node.previousSibling = ps;
2698             ps.nextSibling = node;
2699         }else{
2700             node.previousSibling = null;
2701         }
2702         node.nextSibling = refNode;
2703         refNode.previousSibling = node;
2704         node.setOwnerTree(this.getOwnerTree());
2705         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2706         if(oldParent){
2707             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2708         }
2709         return node;
2710     },
2711
2712     /**
2713      * Returns the child node at the specified index.
2714      * @param {Number} index
2715      * @return {Node}
2716      */
2717     item : function(index){
2718         return this.childNodes[index];
2719     },
2720
2721     /**
2722      * Replaces one child node in this node with another.
2723      * @param {Node} newChild The replacement node
2724      * @param {Node} oldChild The node to replace
2725      * @return {Node} The replaced node
2726      */
2727     replaceChild : function(newChild, oldChild){
2728         this.insertBefore(newChild, oldChild);
2729         this.removeChild(oldChild);
2730         return oldChild;
2731     },
2732
2733     /**
2734      * Returns the index of a child node
2735      * @param {Node} node
2736      * @return {Number} The index of the node or -1 if it was not found
2737      */
2738     indexOf : function(child){
2739         return this.childNodes.indexOf(child);
2740     },
2741
2742     /**
2743      * Returns the tree this node is in.
2744      * @return {Tree}
2745      */
2746     getOwnerTree : function(){
2747         // if it doesn't have one, look for one
2748         if(!this.ownerTree){
2749             var p = this;
2750             while(p){
2751                 if(p.ownerTree){
2752                     this.ownerTree = p.ownerTree;
2753                     break;
2754                 }
2755                 p = p.parentNode;
2756             }
2757         }
2758         return this.ownerTree;
2759     },
2760
2761     /**
2762      * Returns depth of this node (the root node has a depth of 0)
2763      * @return {Number}
2764      */
2765     getDepth : function(){
2766         var depth = 0;
2767         var p = this;
2768         while(p.parentNode){
2769             ++depth;
2770             p = p.parentNode;
2771         }
2772         return depth;
2773     },
2774
2775     // private
2776     setOwnerTree : function(tree){
2777         // if it's move, we need to update everyone
2778         if(tree != this.ownerTree){
2779             if(this.ownerTree){
2780                 this.ownerTree.unregisterNode(this);
2781             }
2782             this.ownerTree = tree;
2783             var cs = this.childNodes;
2784             for(var i = 0, len = cs.length; i < len; i++) {
2785                 cs[i].setOwnerTree(tree);
2786             }
2787             if(tree){
2788                 tree.registerNode(this);
2789             }
2790         }
2791     },
2792
2793     /**
2794      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2795      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2796      * @return {String} The path
2797      */
2798     getPath : function(attr){
2799         attr = attr || "id";
2800         var p = this.parentNode;
2801         var b = [this.attributes[attr]];
2802         while(p){
2803             b.unshift(p.attributes[attr]);
2804             p = p.parentNode;
2805         }
2806         var sep = this.getOwnerTree().pathSeparator;
2807         return sep + b.join(sep);
2808     },
2809
2810     /**
2811      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2812      * function call will be the scope provided or the current node. The arguments to the function
2813      * will be the args provided or the current node. If the function returns false at any point,
2814      * the bubble is stopped.
2815      * @param {Function} fn The function to call
2816      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2817      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2818      */
2819     bubble : function(fn, scope, args){
2820         var p = this;
2821         while(p){
2822             if(fn.call(scope || p, args || p) === false){
2823                 break;
2824             }
2825             p = p.parentNode;
2826         }
2827     },
2828
2829     /**
2830      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2831      * function call will be the scope provided or the current node. The arguments to the function
2832      * will be the args provided or the current node. If the function returns false at any point,
2833      * the cascade is stopped on that branch.
2834      * @param {Function} fn The function to call
2835      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2836      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2837      */
2838     cascade : function(fn, scope, args){
2839         if(fn.call(scope || this, args || this) !== false){
2840             var cs = this.childNodes;
2841             for(var i = 0, len = cs.length; i < len; i++) {
2842                 cs[i].cascade(fn, scope, args);
2843             }
2844         }
2845     },
2846
2847     /**
2848      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2849      * function call will be the scope provided or the current node. The arguments to the function
2850      * will be the args provided or the current node. If the function returns false at any point,
2851      * the iteration stops.
2852      * @param {Function} fn The function to call
2853      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2854      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2855      */
2856     eachChild : function(fn, scope, args){
2857         var cs = this.childNodes;
2858         for(var i = 0, len = cs.length; i < len; i++) {
2859                 if(fn.call(scope || this, args || cs[i]) === false){
2860                     break;
2861                 }
2862         }
2863     },
2864
2865     /**
2866      * Finds the first child that has the attribute with the specified value.
2867      * @param {String} attribute The attribute name
2868      * @param {Mixed} value The value to search for
2869      * @return {Node} The found child or null if none was found
2870      */
2871     findChild : function(attribute, value){
2872         var cs = this.childNodes;
2873         for(var i = 0, len = cs.length; i < len; i++) {
2874                 if(cs[i].attributes[attribute] == value){
2875                     return cs[i];
2876                 }
2877         }
2878         return null;
2879     },
2880
2881     /**
2882      * Finds the first child by a custom function. The child matches if the function passed
2883      * returns true.
2884      * @param {Function} fn
2885      * @param {Object} scope (optional)
2886      * @return {Node} The found child or null if none was found
2887      */
2888     findChildBy : function(fn, scope){
2889         var cs = this.childNodes;
2890         for(var i = 0, len = cs.length; i < len; i++) {
2891                 if(fn.call(scope||cs[i], cs[i]) === true){
2892                     return cs[i];
2893                 }
2894         }
2895         return null;
2896     },
2897
2898     /**
2899      * Sorts this nodes children using the supplied sort function
2900      * @param {Function} fn
2901      * @param {Object} scope (optional)
2902      */
2903     sort : function(fn, scope){
2904         var cs = this.childNodes;
2905         var len = cs.length;
2906         if(len > 0){
2907             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2908             cs.sort(sortFn);
2909             for(var i = 0; i < len; i++){
2910                 var n = cs[i];
2911                 n.previousSibling = cs[i-1];
2912                 n.nextSibling = cs[i+1];
2913                 if(i == 0){
2914                     this.setFirstChild(n);
2915                 }
2916                 if(i == len-1){
2917                     this.setLastChild(n);
2918                 }
2919             }
2920         }
2921     },
2922
2923     /**
2924      * Returns true if this node is an ancestor (at any point) of the passed node.
2925      * @param {Node} node
2926      * @return {Boolean}
2927      */
2928     contains : function(node){
2929         return node.isAncestor(this);
2930     },
2931
2932     /**
2933      * Returns true if the passed node is an ancestor (at any point) of this node.
2934      * @param {Node} node
2935      * @return {Boolean}
2936      */
2937     isAncestor : function(node){
2938         var p = this.parentNode;
2939         while(p){
2940             if(p == node){
2941                 return true;
2942             }
2943             p = p.parentNode;
2944         }
2945         return false;
2946     },
2947
2948     toString : function(){
2949         return "[Node"+(this.id?" "+this.id:"")+"]";
2950     }
2951 });/*
2952  * Based on:
2953  * Ext JS Library 1.1.1
2954  * Copyright(c) 2006-2007, Ext JS, LLC.
2955  *
2956  * Originally Released Under LGPL - original licence link has changed is not relivant.
2957  *
2958  * Fork - LGPL
2959  * <script type="text/javascript">
2960  */
2961  (function(){ 
2962 /**
2963  * @class Roo.Layer
2964  * @extends Roo.Element
2965  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
2966  * automatic maintaining of shadow/shim positions.
2967  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
2968  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
2969  * you can pass a string with a CSS class name. False turns off the shadow.
2970  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
2971  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
2972  * @cfg {String} cls CSS class to add to the element
2973  * @cfg {Number} zindex Starting z-index (defaults to 11000)
2974  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
2975  * @constructor
2976  * @param {Object} config An object with config options.
2977  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
2978  */
2979
2980 Roo.Layer = function(config, existingEl){
2981     config = config || {};
2982     var dh = Roo.DomHelper;
2983     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
2984     if(existingEl){
2985         this.dom = Roo.getDom(existingEl);
2986     }
2987     if(!this.dom){
2988         var o = config.dh || {tag: "div", cls: "x-layer"};
2989         this.dom = dh.append(pel, o);
2990     }
2991     if(config.cls){
2992         this.addClass(config.cls);
2993     }
2994     this.constrain = config.constrain !== false;
2995     this.visibilityMode = Roo.Element.VISIBILITY;
2996     if(config.id){
2997         this.id = this.dom.id = config.id;
2998     }else{
2999         this.id = Roo.id(this.dom);
3000     }
3001     this.zindex = config.zindex || this.getZIndex();
3002     this.position("absolute", this.zindex);
3003     if(config.shadow){
3004         this.shadowOffset = config.shadowOffset || 4;
3005         this.shadow = new Roo.Shadow({
3006             offset : this.shadowOffset,
3007             mode : config.shadow
3008         });
3009     }else{
3010         this.shadowOffset = 0;
3011     }
3012     this.useShim = config.shim !== false && Roo.useShims;
3013     this.useDisplay = config.useDisplay;
3014     this.hide();
3015 };
3016
3017 var supr = Roo.Element.prototype;
3018
3019 // shims are shared among layer to keep from having 100 iframes
3020 var shims = [];
3021
3022 Roo.extend(Roo.Layer, Roo.Element, {
3023
3024     getZIndex : function(){
3025         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
3026     },
3027
3028     getShim : function(){
3029         if(!this.useShim){
3030             return null;
3031         }
3032         if(this.shim){
3033             return this.shim;
3034         }
3035         var shim = shims.shift();
3036         if(!shim){
3037             shim = this.createShim();
3038             shim.enableDisplayMode('block');
3039             shim.dom.style.display = 'none';
3040             shim.dom.style.visibility = 'visible';
3041         }
3042         var pn = this.dom.parentNode;
3043         if(shim.dom.parentNode != pn){
3044             pn.insertBefore(shim.dom, this.dom);
3045         }
3046         shim.setStyle('z-index', this.getZIndex()-2);
3047         this.shim = shim;
3048         return shim;
3049     },
3050
3051     hideShim : function(){
3052         if(this.shim){
3053             this.shim.setDisplayed(false);
3054             shims.push(this.shim);
3055             delete this.shim;
3056         }
3057     },
3058
3059     disableShadow : function(){
3060         if(this.shadow){
3061             this.shadowDisabled = true;
3062             this.shadow.hide();
3063             this.lastShadowOffset = this.shadowOffset;
3064             this.shadowOffset = 0;
3065         }
3066     },
3067
3068     enableShadow : function(show){
3069         if(this.shadow){
3070             this.shadowDisabled = false;
3071             this.shadowOffset = this.lastShadowOffset;
3072             delete this.lastShadowOffset;
3073             if(show){
3074                 this.sync(true);
3075             }
3076         }
3077     },
3078
3079     // private
3080     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
3081     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
3082     sync : function(doShow){
3083         var sw = this.shadow;
3084         if(!this.updating && this.isVisible() && (sw || this.useShim)){
3085             var sh = this.getShim();
3086
3087             var w = this.getWidth(),
3088                 h = this.getHeight();
3089
3090             var l = this.getLeft(true),
3091                 t = this.getTop(true);
3092
3093             if(sw && !this.shadowDisabled){
3094                 if(doShow && !sw.isVisible()){
3095                     sw.show(this);
3096                 }else{
3097                     sw.realign(l, t, w, h);
3098                 }
3099                 if(sh){
3100                     if(doShow){
3101                        sh.show();
3102                     }
3103                     // fit the shim behind the shadow, so it is shimmed too
3104                     var a = sw.adjusts, s = sh.dom.style;
3105                     s.left = (Math.min(l, l+a.l))+"px";
3106                     s.top = (Math.min(t, t+a.t))+"px";
3107                     s.width = (w+a.w)+"px";
3108                     s.height = (h+a.h)+"px";
3109                 }
3110             }else if(sh){
3111                 if(doShow){
3112                    sh.show();
3113                 }
3114                 sh.setSize(w, h);
3115                 sh.setLeftTop(l, t);
3116             }
3117             
3118         }
3119     },
3120
3121     // private
3122     destroy : function(){
3123         this.hideShim();
3124         if(this.shadow){
3125             this.shadow.hide();
3126         }
3127         this.removeAllListeners();
3128         var pn = this.dom.parentNode;
3129         if(pn){
3130             pn.removeChild(this.dom);
3131         }
3132         Roo.Element.uncache(this.id);
3133     },
3134
3135     remove : function(){
3136         this.destroy();
3137     },
3138
3139     // private
3140     beginUpdate : function(){
3141         this.updating = true;
3142     },
3143
3144     // private
3145     endUpdate : function(){
3146         this.updating = false;
3147         this.sync(true);
3148     },
3149
3150     // private
3151     hideUnders : function(negOffset){
3152         if(this.shadow){
3153             this.shadow.hide();
3154         }
3155         this.hideShim();
3156     },
3157
3158     // private
3159     constrainXY : function(){
3160         if(this.constrain){
3161             var vw = Roo.lib.Dom.getViewWidth(),
3162                 vh = Roo.lib.Dom.getViewHeight();
3163             var s = Roo.get(document).getScroll();
3164
3165             var xy = this.getXY();
3166             var x = xy[0], y = xy[1];   
3167             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
3168             // only move it if it needs it
3169             var moved = false;
3170             // first validate right/bottom
3171             if((x + w) > vw+s.left){
3172                 x = vw - w - this.shadowOffset;
3173                 moved = true;
3174             }
3175             if((y + h) > vh+s.top){
3176                 y = vh - h - this.shadowOffset;
3177                 moved = true;
3178             }
3179             // then make sure top/left isn't negative
3180             if(x < s.left){
3181                 x = s.left;
3182                 moved = true;
3183             }
3184             if(y < s.top){
3185                 y = s.top;
3186                 moved = true;
3187             }
3188             if(moved){
3189                 if(this.avoidY){
3190                     var ay = this.avoidY;
3191                     if(y <= ay && (y+h) >= ay){
3192                         y = ay-h-5;   
3193                     }
3194                 }
3195                 xy = [x, y];
3196                 this.storeXY(xy);
3197                 supr.setXY.call(this, xy);
3198                 this.sync();
3199             }
3200         }
3201     },
3202
3203     isVisible : function(){
3204         return this.visible;    
3205     },
3206
3207     // private
3208     showAction : function(){
3209         this.visible = true; // track visibility to prevent getStyle calls
3210         if(this.useDisplay === true){
3211             this.setDisplayed("");
3212         }else if(this.lastXY){
3213             supr.setXY.call(this, this.lastXY);
3214         }else if(this.lastLT){
3215             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
3216         }
3217     },
3218
3219     // private
3220     hideAction : function(){
3221         this.visible = false;
3222         if(this.useDisplay === true){
3223             this.setDisplayed(false);
3224         }else{
3225             this.setLeftTop(-10000,-10000);
3226         }
3227     },
3228
3229     // overridden Element method
3230     setVisible : function(v, a, d, c, e){
3231         if(v){
3232             this.showAction();
3233         }
3234         if(a && v){
3235             var cb = function(){
3236                 this.sync(true);
3237                 if(c){
3238                     c();
3239                 }
3240             }.createDelegate(this);
3241             supr.setVisible.call(this, true, true, d, cb, e);
3242         }else{
3243             if(!v){
3244                 this.hideUnders(true);
3245             }
3246             var cb = c;
3247             if(a){
3248                 cb = function(){
3249                     this.hideAction();
3250                     if(c){
3251                         c();
3252                     }
3253                 }.createDelegate(this);
3254             }
3255             supr.setVisible.call(this, v, a, d, cb, e);
3256             if(v){
3257                 this.sync(true);
3258             }else if(!a){
3259                 this.hideAction();
3260             }
3261         }
3262     },
3263
3264     storeXY : function(xy){
3265         delete this.lastLT;
3266         this.lastXY = xy;
3267     },
3268
3269     storeLeftTop : function(left, top){
3270         delete this.lastXY;
3271         this.lastLT = [left, top];
3272     },
3273
3274     // private
3275     beforeFx : function(){
3276         this.beforeAction();
3277         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
3278     },
3279
3280     // private
3281     afterFx : function(){
3282         Roo.Layer.superclass.afterFx.apply(this, arguments);
3283         this.sync(this.isVisible());
3284     },
3285
3286     // private
3287     beforeAction : function(){
3288         if(!this.updating && this.shadow){
3289             this.shadow.hide();
3290         }
3291     },
3292
3293     // overridden Element method
3294     setLeft : function(left){
3295         this.storeLeftTop(left, this.getTop(true));
3296         supr.setLeft.apply(this, arguments);
3297         this.sync();
3298     },
3299
3300     setTop : function(top){
3301         this.storeLeftTop(this.getLeft(true), top);
3302         supr.setTop.apply(this, arguments);
3303         this.sync();
3304     },
3305
3306     setLeftTop : function(left, top){
3307         this.storeLeftTop(left, top);
3308         supr.setLeftTop.apply(this, arguments);
3309         this.sync();
3310     },
3311
3312     setXY : function(xy, a, d, c, e){
3313         this.fixDisplay();
3314         this.beforeAction();
3315         this.storeXY(xy);
3316         var cb = this.createCB(c);
3317         supr.setXY.call(this, xy, a, d, cb, e);
3318         if(!a){
3319             cb();
3320         }
3321     },
3322
3323     // private
3324     createCB : function(c){
3325         var el = this;
3326         return function(){
3327             el.constrainXY();
3328             el.sync(true);
3329             if(c){
3330                 c();
3331             }
3332         };
3333     },
3334
3335     // overridden Element method
3336     setX : function(x, a, d, c, e){
3337         this.setXY([x, this.getY()], a, d, c, e);
3338     },
3339
3340     // overridden Element method
3341     setY : function(y, a, d, c, e){
3342         this.setXY([this.getX(), y], a, d, c, e);
3343     },
3344
3345     // overridden Element method
3346     setSize : function(w, h, a, d, c, e){
3347         this.beforeAction();
3348         var cb = this.createCB(c);
3349         supr.setSize.call(this, w, h, a, d, cb, e);
3350         if(!a){
3351             cb();
3352         }
3353     },
3354
3355     // overridden Element method
3356     setWidth : function(w, a, d, c, e){
3357         this.beforeAction();
3358         var cb = this.createCB(c);
3359         supr.setWidth.call(this, w, a, d, cb, e);
3360         if(!a){
3361             cb();
3362         }
3363     },
3364
3365     // overridden Element method
3366     setHeight : function(h, a, d, c, e){
3367         this.beforeAction();
3368         var cb = this.createCB(c);
3369         supr.setHeight.call(this, h, a, d, cb, e);
3370         if(!a){
3371             cb();
3372         }
3373     },
3374
3375     // overridden Element method
3376     setBounds : function(x, y, w, h, a, d, c, e){
3377         this.beforeAction();
3378         var cb = this.createCB(c);
3379         if(!a){
3380             this.storeXY([x, y]);
3381             supr.setXY.call(this, [x, y]);
3382             supr.setSize.call(this, w, h, a, d, cb, e);
3383             cb();
3384         }else{
3385             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
3386         }
3387         return this;
3388     },
3389     
3390     /**
3391      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
3392      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
3393      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
3394      * @param {Number} zindex The new z-index to set
3395      * @return {this} The Layer
3396      */
3397     setZIndex : function(zindex){
3398         this.zindex = zindex;
3399         this.setStyle("z-index", zindex + 2);
3400         if(this.shadow){
3401             this.shadow.setZIndex(zindex + 1);
3402         }
3403         if(this.shim){
3404             this.shim.setStyle("z-index", zindex);
3405         }
3406     }
3407 });
3408 })();/*
3409  * Based on:
3410  * Ext JS Library 1.1.1
3411  * Copyright(c) 2006-2007, Ext JS, LLC.
3412  *
3413  * Originally Released Under LGPL - original licence link has changed is not relivant.
3414  *
3415  * Fork - LGPL
3416  * <script type="text/javascript">
3417  */
3418
3419
3420 /**
3421  * @class Roo.Shadow
3422  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
3423  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
3424  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
3425  * @constructor
3426  * Create a new Shadow
3427  * @param {Object} config The config object
3428  */
3429 Roo.Shadow = function(config){
3430     Roo.apply(this, config);
3431     if(typeof this.mode != "string"){
3432         this.mode = this.defaultMode;
3433     }
3434     var o = this.offset, a = {h: 0};
3435     var rad = Math.floor(this.offset/2);
3436     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3437         case "drop":
3438             a.w = 0;
3439             a.l = a.t = o;
3440             a.t -= 1;
3441             if(Roo.isIE){
3442                 a.l -= this.offset + rad;
3443                 a.t -= this.offset + rad;
3444                 a.w -= rad;
3445                 a.h -= rad;
3446                 a.t += 1;
3447             }
3448         break;
3449         case "sides":
3450             a.w = (o*2);
3451             a.l = -o;
3452             a.t = o-1;
3453             if(Roo.isIE){
3454                 a.l -= (this.offset - rad);
3455                 a.t -= this.offset + rad;
3456                 a.l += 1;
3457                 a.w -= (this.offset - rad)*2;
3458                 a.w -= rad + 1;
3459                 a.h -= 1;
3460             }
3461         break;
3462         case "frame":
3463             a.w = a.h = (o*2);
3464             a.l = a.t = -o;
3465             a.t += 1;
3466             a.h -= 2;
3467             if(Roo.isIE){
3468                 a.l -= (this.offset - rad);
3469                 a.t -= (this.offset - rad);
3470                 a.l += 1;
3471                 a.w -= (this.offset + rad + 1);
3472                 a.h -= (this.offset + rad);
3473                 a.h += 1;
3474             }
3475         break;
3476     };
3477
3478     this.adjusts = a;
3479 };
3480
3481 Roo.Shadow.prototype = {
3482     /**
3483      * @cfg {String} mode
3484      * The shadow display mode.  Supports the following options:<br />
3485      * sides: Shadow displays on both sides and bottom only<br />
3486      * frame: Shadow displays equally on all four sides<br />
3487      * drop: Traditional bottom-right drop shadow (default)
3488      */
3489     /**
3490      * @cfg {String} offset
3491      * The number of pixels to offset the shadow from the element (defaults to 4)
3492      */
3493     offset: 4,
3494
3495     // private
3496     defaultMode: "drop",
3497
3498     /**
3499      * Displays the shadow under the target element
3500      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3501      */
3502     show : function(target){
3503         target = Roo.get(target);
3504         if(!this.el){
3505             this.el = Roo.Shadow.Pool.pull();
3506             if(this.el.dom.nextSibling != target.dom){
3507                 this.el.insertBefore(target);
3508             }
3509         }
3510         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3511         if(Roo.isIE){
3512             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3513         }
3514         this.realign(
3515             target.getLeft(true),
3516             target.getTop(true),
3517             target.getWidth(),
3518             target.getHeight()
3519         );
3520         this.el.dom.style.display = "block";
3521     },
3522
3523     /**
3524      * Returns true if the shadow is visible, else false
3525      */
3526     isVisible : function(){
3527         return this.el ? true : false;  
3528     },
3529
3530     /**
3531      * Direct alignment when values are already available. Show must be called at least once before
3532      * calling this method to ensure it is initialized.
3533      * @param {Number} left The target element left position
3534      * @param {Number} top The target element top position
3535      * @param {Number} width The target element width
3536      * @param {Number} height The target element height
3537      */
3538     realign : function(l, t, w, h){
3539         if(!this.el){
3540             return;
3541         }
3542         var a = this.adjusts, d = this.el.dom, s = d.style;
3543         var iea = 0;
3544         s.left = (l+a.l)+"px";
3545         s.top = (t+a.t)+"px";
3546         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3547  
3548         if(s.width != sws || s.height != shs){
3549             s.width = sws;
3550             s.height = shs;
3551             if(!Roo.isIE){
3552                 var cn = d.childNodes;
3553                 var sww = Math.max(0, (sw-12))+"px";
3554                 cn[0].childNodes[1].style.width = sww;
3555                 cn[1].childNodes[1].style.width = sww;
3556                 cn[2].childNodes[1].style.width = sww;
3557                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3558             }
3559         }
3560     },
3561
3562     /**
3563      * Hides this shadow
3564      */
3565     hide : function(){
3566         if(this.el){
3567             this.el.dom.style.display = "none";
3568             Roo.Shadow.Pool.push(this.el);
3569             delete this.el;
3570         }
3571     },
3572
3573     /**
3574      * Adjust the z-index of this shadow
3575      * @param {Number} zindex The new z-index
3576      */
3577     setZIndex : function(z){
3578         this.zIndex = z;
3579         if(this.el){
3580             this.el.setStyle("z-index", z);
3581         }
3582     }
3583 };
3584
3585 // Private utility class that manages the internal Shadow cache
3586 Roo.Shadow.Pool = function(){
3587     var p = [];
3588     var markup = Roo.isIE ?
3589                  '<div class="x-ie-shadow"></div>' :
3590                  '<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>';
3591     return {
3592         pull : function(){
3593             var sh = p.shift();
3594             if(!sh){
3595                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3596                 sh.autoBoxAdjust = false;
3597             }
3598             return sh;
3599         },
3600
3601         push : function(sh){
3602             p.push(sh);
3603         }
3604     };
3605 }();/*
3606  * Based on:
3607  * Ext JS Library 1.1.1
3608  * Copyright(c) 2006-2007, Ext JS, LLC.
3609  *
3610  * Originally Released Under LGPL - original licence link has changed is not relivant.
3611  *
3612  * Fork - LGPL
3613  * <script type="text/javascript">
3614  */
3615
3616
3617 /**
3618  * @class Roo.SplitBar
3619  * @extends Roo.util.Observable
3620  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3621  * <br><br>
3622  * Usage:
3623  * <pre><code>
3624 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3625                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3626 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3627 split.minSize = 100;
3628 split.maxSize = 600;
3629 split.animate = true;
3630 split.on('moved', splitterMoved);
3631 </code></pre>
3632  * @constructor
3633  * Create a new SplitBar
3634  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3635  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3636  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3637  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3638                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3639                         position of the SplitBar).
3640  */
3641 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3642     
3643     /** @private */
3644     this.el = Roo.get(dragElement, true);
3645     this.el.dom.unselectable = "on";
3646     /** @private */
3647     this.resizingEl = Roo.get(resizingElement, true);
3648
3649     /**
3650      * @private
3651      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3652      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3653      * @type Number
3654      */
3655     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3656     
3657     /**
3658      * The minimum size of the resizing element. (Defaults to 0)
3659      * @type Number
3660      */
3661     this.minSize = 0;
3662     
3663     /**
3664      * The maximum size of the resizing element. (Defaults to 2000)
3665      * @type Number
3666      */
3667     this.maxSize = 2000;
3668     
3669     /**
3670      * Whether to animate the transition to the new size
3671      * @type Boolean
3672      */
3673     this.animate = false;
3674     
3675     /**
3676      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3677      * @type Boolean
3678      */
3679     this.useShim = false;
3680     
3681     /** @private */
3682     this.shim = null;
3683     
3684     if(!existingProxy){
3685         /** @private */
3686         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3687     }else{
3688         this.proxy = Roo.get(existingProxy).dom;
3689     }
3690     /** @private */
3691     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3692     
3693     /** @private */
3694     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3695     
3696     /** @private */
3697     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3698     
3699     /** @private */
3700     this.dragSpecs = {};
3701     
3702     /**
3703      * @private The adapter to use to positon and resize elements
3704      */
3705     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3706     this.adapter.init(this);
3707     
3708     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3709         /** @private */
3710         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3711         this.el.addClass("x-splitbar-h");
3712     }else{
3713         /** @private */
3714         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3715         this.el.addClass("x-splitbar-v");
3716     }
3717     
3718     this.addEvents({
3719         /**
3720          * @event resize
3721          * Fires when the splitter is moved (alias for {@link #event-moved})
3722          * @param {Roo.SplitBar} this
3723          * @param {Number} newSize the new width or height
3724          */
3725         "resize" : true,
3726         /**
3727          * @event moved
3728          * Fires when the splitter is moved
3729          * @param {Roo.SplitBar} this
3730          * @param {Number} newSize the new width or height
3731          */
3732         "moved" : true,
3733         /**
3734          * @event beforeresize
3735          * Fires before the splitter is dragged
3736          * @param {Roo.SplitBar} this
3737          */
3738         "beforeresize" : true,
3739
3740         "beforeapply" : true
3741     });
3742
3743     Roo.util.Observable.call(this);
3744 };
3745
3746 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3747     onStartProxyDrag : function(x, y){
3748         this.fireEvent("beforeresize", this);
3749         if(!this.overlay){
3750             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3751             o.unselectable();
3752             o.enableDisplayMode("block");
3753             // all splitbars share the same overlay
3754             Roo.SplitBar.prototype.overlay = o;
3755         }
3756         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3757         this.overlay.show();
3758         Roo.get(this.proxy).setDisplayed("block");
3759         var size = this.adapter.getElementSize(this);
3760         this.activeMinSize = this.getMinimumSize();;
3761         this.activeMaxSize = this.getMaximumSize();;
3762         var c1 = size - this.activeMinSize;
3763         var c2 = Math.max(this.activeMaxSize - size, 0);
3764         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3765             this.dd.resetConstraints();
3766             this.dd.setXConstraint(
3767                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3768                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3769             );
3770             this.dd.setYConstraint(0, 0);
3771         }else{
3772             this.dd.resetConstraints();
3773             this.dd.setXConstraint(0, 0);
3774             this.dd.setYConstraint(
3775                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3776                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3777             );
3778          }
3779         this.dragSpecs.startSize = size;
3780         this.dragSpecs.startPoint = [x, y];
3781         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3782     },
3783     
3784     /** 
3785      * @private Called after the drag operation by the DDProxy
3786      */
3787     onEndProxyDrag : function(e){
3788         Roo.get(this.proxy).setDisplayed(false);
3789         var endPoint = Roo.lib.Event.getXY(e);
3790         if(this.overlay){
3791             this.overlay.hide();
3792         }
3793         var newSize;
3794         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3795             newSize = this.dragSpecs.startSize + 
3796                 (this.placement == Roo.SplitBar.LEFT ?
3797                     endPoint[0] - this.dragSpecs.startPoint[0] :
3798                     this.dragSpecs.startPoint[0] - endPoint[0]
3799                 );
3800         }else{
3801             newSize = this.dragSpecs.startSize + 
3802                 (this.placement == Roo.SplitBar.TOP ?
3803                     endPoint[1] - this.dragSpecs.startPoint[1] :
3804                     this.dragSpecs.startPoint[1] - endPoint[1]
3805                 );
3806         }
3807         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3808         if(newSize != this.dragSpecs.startSize){
3809             if(this.fireEvent('beforeapply', this, newSize) !== false){
3810                 this.adapter.setElementSize(this, newSize);
3811                 this.fireEvent("moved", this, newSize);
3812                 this.fireEvent("resize", this, newSize);
3813             }
3814         }
3815     },
3816     
3817     /**
3818      * Get the adapter this SplitBar uses
3819      * @return The adapter object
3820      */
3821     getAdapter : function(){
3822         return this.adapter;
3823     },
3824     
3825     /**
3826      * Set the adapter this SplitBar uses
3827      * @param {Object} adapter A SplitBar adapter object
3828      */
3829     setAdapter : function(adapter){
3830         this.adapter = adapter;
3831         this.adapter.init(this);
3832     },
3833     
3834     /**
3835      * Gets the minimum size for the resizing element
3836      * @return {Number} The minimum size
3837      */
3838     getMinimumSize : function(){
3839         return this.minSize;
3840     },
3841     
3842     /**
3843      * Sets the minimum size for the resizing element
3844      * @param {Number} minSize The minimum size
3845      */
3846     setMinimumSize : function(minSize){
3847         this.minSize = minSize;
3848     },
3849     
3850     /**
3851      * Gets the maximum size for the resizing element
3852      * @return {Number} The maximum size
3853      */
3854     getMaximumSize : function(){
3855         return this.maxSize;
3856     },
3857     
3858     /**
3859      * Sets the maximum size for the resizing element
3860      * @param {Number} maxSize The maximum size
3861      */
3862     setMaximumSize : function(maxSize){
3863         this.maxSize = maxSize;
3864     },
3865     
3866     /**
3867      * Sets the initialize size for the resizing element
3868      * @param {Number} size The initial size
3869      */
3870     setCurrentSize : function(size){
3871         var oldAnimate = this.animate;
3872         this.animate = false;
3873         this.adapter.setElementSize(this, size);
3874         this.animate = oldAnimate;
3875     },
3876     
3877     /**
3878      * Destroy this splitbar. 
3879      * @param {Boolean} removeEl True to remove the element
3880      */
3881     destroy : function(removeEl){
3882         if(this.shim){
3883             this.shim.remove();
3884         }
3885         this.dd.unreg();
3886         this.proxy.parentNode.removeChild(this.proxy);
3887         if(removeEl){
3888             this.el.remove();
3889         }
3890     }
3891 });
3892
3893 /**
3894  * @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.
3895  */
3896 Roo.SplitBar.createProxy = function(dir){
3897     var proxy = new Roo.Element(document.createElement("div"));
3898     proxy.unselectable();
3899     var cls = 'x-splitbar-proxy';
3900     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3901     document.body.appendChild(proxy.dom);
3902     return proxy.dom;
3903 };
3904
3905 /** 
3906  * @class Roo.SplitBar.BasicLayoutAdapter
3907  * Default Adapter. It assumes the splitter and resizing element are not positioned
3908  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3909  */
3910 Roo.SplitBar.BasicLayoutAdapter = function(){
3911 };
3912
3913 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3914     // do nothing for now
3915     init : function(s){
3916     
3917     },
3918     /**
3919      * Called before drag operations to get the current size of the resizing element. 
3920      * @param {Roo.SplitBar} s The SplitBar using this adapter
3921      */
3922      getElementSize : function(s){
3923         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3924             return s.resizingEl.getWidth();
3925         }else{
3926             return s.resizingEl.getHeight();
3927         }
3928     },
3929     
3930     /**
3931      * Called after drag operations to set the size of the resizing element.
3932      * @param {Roo.SplitBar} s The SplitBar using this adapter
3933      * @param {Number} newSize The new size to set
3934      * @param {Function} onComplete A function to be invoked when resizing is complete
3935      */
3936     setElementSize : function(s, newSize, onComplete){
3937         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3938             if(!s.animate){
3939                 s.resizingEl.setWidth(newSize);
3940                 if(onComplete){
3941                     onComplete(s, newSize);
3942                 }
3943             }else{
3944                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3945             }
3946         }else{
3947             
3948             if(!s.animate){
3949                 s.resizingEl.setHeight(newSize);
3950                 if(onComplete){
3951                     onComplete(s, newSize);
3952                 }
3953             }else{
3954                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3955             }
3956         }
3957     }
3958 };
3959
3960 /** 
3961  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3962  * @extends Roo.SplitBar.BasicLayoutAdapter
3963  * Adapter that  moves the splitter element to align with the resized sizing element. 
3964  * Used with an absolute positioned SplitBar.
3965  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3966  * document.body, make sure you assign an id to the body element.
3967  */
3968 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3969     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3970     this.container = Roo.get(container);
3971 };
3972
3973 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3974     init : function(s){
3975         this.basic.init(s);
3976     },
3977     
3978     getElementSize : function(s){
3979         return this.basic.getElementSize(s);
3980     },
3981     
3982     setElementSize : function(s, newSize, onComplete){
3983         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3984     },
3985     
3986     moveSplitter : function(s){
3987         var yes = Roo.SplitBar;
3988         switch(s.placement){
3989             case yes.LEFT:
3990                 s.el.setX(s.resizingEl.getRight());
3991                 break;
3992             case yes.RIGHT:
3993                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3994                 break;
3995             case yes.TOP:
3996                 s.el.setY(s.resizingEl.getBottom());
3997                 break;
3998             case yes.BOTTOM:
3999                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
4000                 break;
4001         }
4002     }
4003 };
4004
4005 /**
4006  * Orientation constant - Create a vertical SplitBar
4007  * @static
4008  * @type Number
4009  */
4010 Roo.SplitBar.VERTICAL = 1;
4011
4012 /**
4013  * Orientation constant - Create a horizontal SplitBar
4014  * @static
4015  * @type Number
4016  */
4017 Roo.SplitBar.HORIZONTAL = 2;
4018
4019 /**
4020  * Placement constant - The resizing element is to the left of the splitter element
4021  * @static
4022  * @type Number
4023  */
4024 Roo.SplitBar.LEFT = 1;
4025
4026 /**
4027  * Placement constant - The resizing element is to the right of the splitter element
4028  * @static
4029  * @type Number
4030  */
4031 Roo.SplitBar.RIGHT = 2;
4032
4033 /**
4034  * Placement constant - The resizing element is positioned above the splitter element
4035  * @static
4036  * @type Number
4037  */
4038 Roo.SplitBar.TOP = 3;
4039
4040 /**
4041  * Placement constant - The resizing element is positioned under splitter element
4042  * @static
4043  * @type Number
4044  */
4045 Roo.SplitBar.BOTTOM = 4;
4046 /*
4047  * Based on:
4048  * Ext JS Library 1.1.1
4049  * Copyright(c) 2006-2007, Ext JS, LLC.
4050  *
4051  * Originally Released Under LGPL - original licence link has changed is not relivant.
4052  *
4053  * Fork - LGPL
4054  * <script type="text/javascript">
4055  */
4056
4057 /**
4058  * @class Roo.View
4059  * @extends Roo.util.Observable
4060  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
4061  * This class also supports single and multi selection modes. <br>
4062  * Create a data model bound view:
4063  <pre><code>
4064  var store = new Roo.data.Store(...);
4065
4066  var view = new Roo.View({
4067     el : "my-element",
4068     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
4069  
4070     singleSelect: true,
4071     selectedClass: "ydataview-selected",
4072     store: store
4073  });
4074
4075  // listen for node click?
4076  view.on("click", function(vw, index, node, e){
4077  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4078  });
4079
4080  // load XML data
4081  dataModel.load("foobar.xml");
4082  </code></pre>
4083  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
4084  * <br><br>
4085  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
4086  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
4087  * 
4088  * Note: old style constructor is still suported (container, template, config)
4089  * 
4090  * @constructor
4091  * Create a new View
4092  * @param {Object} config The config object
4093  * 
4094  */
4095 Roo.View = function(config, depreciated_tpl, depreciated_config){
4096     
4097     this.parent = false;
4098     
4099     if (typeof(depreciated_tpl) == 'undefined') {
4100         // new way.. - universal constructor.
4101         Roo.apply(this, config);
4102         this.el  = Roo.get(this.el);
4103     } else {
4104         // old format..
4105         this.el  = Roo.get(config);
4106         this.tpl = depreciated_tpl;
4107         Roo.apply(this, depreciated_config);
4108     }
4109     this.wrapEl  = this.el.wrap().wrap();
4110     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
4111     
4112     
4113     if(typeof(this.tpl) == "string"){
4114         this.tpl = new Roo.Template(this.tpl);
4115     } else {
4116         // support xtype ctors..
4117         this.tpl = new Roo.factory(this.tpl, Roo);
4118     }
4119     
4120     
4121     this.tpl.compile();
4122     
4123     /** @private */
4124     this.addEvents({
4125         /**
4126          * @event beforeclick
4127          * Fires before a click is processed. Returns false to cancel the default action.
4128          * @param {Roo.View} this
4129          * @param {Number} index The index of the target node
4130          * @param {HTMLElement} node The target node
4131          * @param {Roo.EventObject} e The raw event object
4132          */
4133             "beforeclick" : true,
4134         /**
4135          * @event click
4136          * Fires when a template node is clicked.
4137          * @param {Roo.View} this
4138          * @param {Number} index The index of the target node
4139          * @param {HTMLElement} node The target node
4140          * @param {Roo.EventObject} e The raw event object
4141          */
4142             "click" : true,
4143         /**
4144          * @event dblclick
4145          * Fires when a template node is double clicked.
4146          * @param {Roo.View} this
4147          * @param {Number} index The index of the target node
4148          * @param {HTMLElement} node The target node
4149          * @param {Roo.EventObject} e The raw event object
4150          */
4151             "dblclick" : true,
4152         /**
4153          * @event contextmenu
4154          * Fires when a template node is right clicked.
4155          * @param {Roo.View} this
4156          * @param {Number} index The index of the target node
4157          * @param {HTMLElement} node The target node
4158          * @param {Roo.EventObject} e The raw event object
4159          */
4160             "contextmenu" : true,
4161         /**
4162          * @event selectionchange
4163          * Fires when the selected nodes change.
4164          * @param {Roo.View} this
4165          * @param {Array} selections Array of the selected nodes
4166          */
4167             "selectionchange" : true,
4168     
4169         /**
4170          * @event beforeselect
4171          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
4172          * @param {Roo.View} this
4173          * @param {HTMLElement} node The node to be selected
4174          * @param {Array} selections Array of currently selected nodes
4175          */
4176             "beforeselect" : true,
4177         /**
4178          * @event preparedata
4179          * Fires on every row to render, to allow you to change the data.
4180          * @param {Roo.View} this
4181          * @param {Object} data to be rendered (change this)
4182          */
4183           "preparedata" : true
4184           
4185           
4186         });
4187
4188
4189
4190     this.el.on({
4191         "click": this.onClick,
4192         "dblclick": this.onDblClick,
4193         "contextmenu": this.onContextMenu,
4194         scope:this
4195     });
4196
4197     this.selections = [];
4198     this.nodes = [];
4199     this.cmp = new Roo.CompositeElementLite([]);
4200     if(this.store){
4201         this.store = Roo.factory(this.store, Roo.data);
4202         this.setStore(this.store, true);
4203     }
4204     
4205     if ( this.footer && this.footer.xtype) {
4206            
4207          var fctr = this.wrapEl.appendChild(document.createElement("div"));
4208         
4209         this.footer.dataSource = this.store;
4210         this.footer.container = fctr;
4211         this.footer = Roo.factory(this.footer, Roo);
4212         fctr.insertFirst(this.el);
4213         
4214         // this is a bit insane - as the paging toolbar seems to detach the el..
4215 //        dom.parentNode.parentNode.parentNode
4216          // they get detached?
4217     }
4218     
4219     
4220     Roo.View.superclass.constructor.call(this);
4221     
4222     
4223 };
4224
4225 Roo.extend(Roo.View, Roo.util.Observable, {
4226     
4227      /**
4228      * @cfg {Roo.data.Store} store Data store to load data from.
4229      */
4230     store : false,
4231     
4232     /**
4233      * @cfg {String|Roo.Element} el The container element.
4234      */
4235     el : '',
4236     
4237     /**
4238      * @cfg {String|Roo.Template} tpl The template used by this View 
4239      */
4240     tpl : false,
4241     /**
4242      * @cfg {String} dataName the named area of the template to use as the data area
4243      *                          Works with domtemplates roo-name="name"
4244      */
4245     dataName: false,
4246     /**
4247      * @cfg {String} selectedClass The css class to add to selected nodes
4248      */
4249     selectedClass : "x-view-selected",
4250      /**
4251      * @cfg {String} emptyText The empty text to show when nothing is loaded.
4252      */
4253     emptyText : "",
4254     
4255     /**
4256      * @cfg {String} text to display on mask (default Loading)
4257      */
4258     mask : false,
4259     /**
4260      * @cfg {Boolean} multiSelect Allow multiple selection
4261      */
4262     multiSelect : false,
4263     /**
4264      * @cfg {Boolean} singleSelect Allow single selection
4265      */
4266     singleSelect:  false,
4267     
4268     /**
4269      * @cfg {Boolean} toggleSelect - selecting 
4270      */
4271     toggleSelect : false,
4272     
4273     /**
4274      * @cfg {Boolean} tickable - selecting 
4275      */
4276     tickable : false,
4277     
4278     /**
4279      * Returns the element this view is bound to.
4280      * @return {Roo.Element}
4281      */
4282     getEl : function(){
4283         return this.wrapEl;
4284     },
4285     
4286     
4287
4288     /**
4289      * Refreshes the view. - called by datachanged on the store. - do not call directly.
4290      */
4291     refresh : function(){
4292         //Roo.log('refresh');
4293         var t = this.tpl;
4294         
4295         // if we are using something like 'domtemplate', then
4296         // the what gets used is:
4297         // t.applySubtemplate(NAME, data, wrapping data..)
4298         // the outer template then get' applied with
4299         //     the store 'extra data'
4300         // and the body get's added to the
4301         //      roo-name="data" node?
4302         //      <span class='roo-tpl-{name}'></span> ?????
4303         
4304         
4305         
4306         this.clearSelections();
4307         this.el.update("");
4308         var html = [];
4309         var records = this.store.getRange();
4310         if(records.length < 1) {
4311             
4312             // is this valid??  = should it render a template??
4313             
4314             this.el.update(this.emptyText);
4315             return;
4316         }
4317         var el = this.el;
4318         if (this.dataName) {
4319             this.el.update(t.apply(this.store.meta)); //????
4320             el = this.el.child('.roo-tpl-' + this.dataName);
4321         }
4322         
4323         for(var i = 0, len = records.length; i < len; i++){
4324             var data = this.prepareData(records[i].data, i, records[i]);
4325             this.fireEvent("preparedata", this, data, i, records[i]);
4326             
4327             var d = Roo.apply({}, data);
4328             
4329             if(this.tickable){
4330                 Roo.apply(d, {'roo-id' : Roo.id()});
4331                 
4332                 var _this = this;
4333             
4334                 Roo.each(this.parent.item, function(item){
4335                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
4336                         return;
4337                     }
4338                     Roo.apply(d, {'roo-data-checked' : 'checked'});
4339                 });
4340             }
4341             
4342             html[html.length] = Roo.util.Format.trim(
4343                 this.dataName ?
4344                     t.applySubtemplate(this.dataName, d, this.store.meta) :
4345                     t.apply(d)
4346             );
4347         }
4348         
4349         
4350         
4351         el.update(html.join(""));
4352         this.nodes = el.dom.childNodes;
4353         this.updateIndexes(0);
4354     },
4355     
4356
4357     /**
4358      * Function to override to reformat the data that is sent to
4359      * the template for each node.
4360      * DEPRICATED - use the preparedata event handler.
4361      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
4362      * a JSON object for an UpdateManager bound view).
4363      */
4364     prepareData : function(data, index, record)
4365     {
4366         this.fireEvent("preparedata", this, data, index, record);
4367         return data;
4368     },
4369
4370     onUpdate : function(ds, record){
4371         // Roo.log('on update');   
4372         this.clearSelections();
4373         var index = this.store.indexOf(record);
4374         var n = this.nodes[index];
4375         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
4376         n.parentNode.removeChild(n);
4377         this.updateIndexes(index, index);
4378     },
4379
4380     
4381     
4382 // --------- FIXME     
4383     onAdd : function(ds, records, index)
4384     {
4385         //Roo.log(['on Add', ds, records, index] );        
4386         this.clearSelections();
4387         if(this.nodes.length == 0){
4388             this.refresh();
4389             return;
4390         }
4391         var n = this.nodes[index];
4392         for(var i = 0, len = records.length; i < len; i++){
4393             var d = this.prepareData(records[i].data, i, records[i]);
4394             if(n){
4395                 this.tpl.insertBefore(n, d);
4396             }else{
4397                 
4398                 this.tpl.append(this.el, d);
4399             }
4400         }
4401         this.updateIndexes(index);
4402     },
4403
4404     onRemove : function(ds, record, index){
4405        // Roo.log('onRemove');
4406         this.clearSelections();
4407         var el = this.dataName  ?
4408             this.el.child('.roo-tpl-' + this.dataName) :
4409             this.el; 
4410         
4411         el.dom.removeChild(this.nodes[index]);
4412         this.updateIndexes(index);
4413     },
4414
4415     /**
4416      * Refresh an individual node.
4417      * @param {Number} index
4418      */
4419     refreshNode : function(index){
4420         this.onUpdate(this.store, this.store.getAt(index));
4421     },
4422
4423     updateIndexes : function(startIndex, endIndex){
4424         var ns = this.nodes;
4425         startIndex = startIndex || 0;
4426         endIndex = endIndex || ns.length - 1;
4427         for(var i = startIndex; i <= endIndex; i++){
4428             ns[i].nodeIndex = i;
4429         }
4430     },
4431
4432     /**
4433      * Changes the data store this view uses and refresh the view.
4434      * @param {Store} store
4435      */
4436     setStore : function(store, initial){
4437         if(!initial && this.store){
4438             this.store.un("datachanged", this.refresh);
4439             this.store.un("add", this.onAdd);
4440             this.store.un("remove", this.onRemove);
4441             this.store.un("update", this.onUpdate);
4442             this.store.un("clear", this.refresh);
4443             this.store.un("beforeload", this.onBeforeLoad);
4444             this.store.un("load", this.onLoad);
4445             this.store.un("loadexception", this.onLoad);
4446         }
4447         if(store){
4448           
4449             store.on("datachanged", this.refresh, this);
4450             store.on("add", this.onAdd, this);
4451             store.on("remove", this.onRemove, this);
4452             store.on("update", this.onUpdate, this);
4453             store.on("clear", this.refresh, this);
4454             store.on("beforeload", this.onBeforeLoad, this);
4455             store.on("load", this.onLoad, this);
4456             store.on("loadexception", this.onLoad, this);
4457         }
4458         
4459         if(store){
4460             this.refresh();
4461         }
4462     },
4463     /**
4464      * onbeforeLoad - masks the loading area.
4465      *
4466      */
4467     onBeforeLoad : function(store,opts)
4468     {
4469          //Roo.log('onBeforeLoad');   
4470         if (!opts.add) {
4471             this.el.update("");
4472         }
4473         this.el.mask(this.mask ? this.mask : "Loading" ); 
4474     },
4475     onLoad : function ()
4476     {
4477         this.el.unmask();
4478     },
4479     
4480
4481     /**
4482      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4483      * @param {HTMLElement} node
4484      * @return {HTMLElement} The template node
4485      */
4486     findItemFromChild : function(node){
4487         var el = this.dataName  ?
4488             this.el.child('.roo-tpl-' + this.dataName,true) :
4489             this.el.dom; 
4490         
4491         if(!node || node.parentNode == el){
4492                     return node;
4493             }
4494             var p = node.parentNode;
4495             while(p && p != el){
4496             if(p.parentNode == el){
4497                 return p;
4498             }
4499             p = p.parentNode;
4500         }
4501             return null;
4502     },
4503
4504     /** @ignore */
4505     onClick : function(e){
4506         var item = this.findItemFromChild(e.getTarget());
4507         if(item){
4508             var index = this.indexOf(item);
4509             if(this.onItemClick(item, index, e) !== false){
4510                 this.fireEvent("click", this, index, item, e);
4511             }
4512         }else{
4513             this.clearSelections();
4514         }
4515     },
4516
4517     /** @ignore */
4518     onContextMenu : function(e){
4519         var item = this.findItemFromChild(e.getTarget());
4520         if(item){
4521             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4522         }
4523     },
4524
4525     /** @ignore */
4526     onDblClick : function(e){
4527         var item = this.findItemFromChild(e.getTarget());
4528         if(item){
4529             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4530         }
4531     },
4532
4533     onItemClick : function(item, index, e)
4534     {
4535         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4536             return false;
4537         }
4538         if (this.toggleSelect) {
4539             var m = this.isSelected(item) ? 'unselect' : 'select';
4540             //Roo.log(m);
4541             var _t = this;
4542             _t[m](item, true, false);
4543             return true;
4544         }
4545         if(this.multiSelect || this.singleSelect){
4546             if(this.multiSelect && e.shiftKey && this.lastSelection){
4547                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4548             }else{
4549                 this.select(item, this.multiSelect && e.ctrlKey);
4550                 this.lastSelection = item;
4551             }
4552             
4553             if(!this.tickable){
4554                 e.preventDefault();
4555             }
4556             
4557         }
4558         return true;
4559     },
4560
4561     /**
4562      * Get the number of selected nodes.
4563      * @return {Number}
4564      */
4565     getSelectionCount : function(){
4566         return this.selections.length;
4567     },
4568
4569     /**
4570      * Get the currently selected nodes.
4571      * @return {Array} An array of HTMLElements
4572      */
4573     getSelectedNodes : function(){
4574         return this.selections;
4575     },
4576
4577     /**
4578      * Get the indexes of the selected nodes.
4579      * @return {Array}
4580      */
4581     getSelectedIndexes : function(){
4582         var indexes = [], s = this.selections;
4583         for(var i = 0, len = s.length; i < len; i++){
4584             indexes.push(s[i].nodeIndex);
4585         }
4586         return indexes;
4587     },
4588
4589     /**
4590      * Clear all selections
4591      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4592      */
4593     clearSelections : function(suppressEvent){
4594         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4595             this.cmp.elements = this.selections;
4596             this.cmp.removeClass(this.selectedClass);
4597             this.selections = [];
4598             if(!suppressEvent){
4599                 this.fireEvent("selectionchange", this, this.selections);
4600             }
4601         }
4602     },
4603
4604     /**
4605      * Returns true if the passed node is selected
4606      * @param {HTMLElement/Number} node The node or node index
4607      * @return {Boolean}
4608      */
4609     isSelected : function(node){
4610         var s = this.selections;
4611         if(s.length < 1){
4612             return false;
4613         }
4614         node = this.getNode(node);
4615         return s.indexOf(node) !== -1;
4616     },
4617
4618     /**
4619      * Selects nodes.
4620      * @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
4621      * @param {Boolean} keepExisting (optional) true to keep existing selections
4622      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4623      */
4624     select : function(nodeInfo, keepExisting, suppressEvent){
4625         if(nodeInfo instanceof Array){
4626             if(!keepExisting){
4627                 this.clearSelections(true);
4628             }
4629             for(var i = 0, len = nodeInfo.length; i < len; i++){
4630                 this.select(nodeInfo[i], true, true);
4631             }
4632             return;
4633         } 
4634         var node = this.getNode(nodeInfo);
4635         if(!node || this.isSelected(node)){
4636             return; // already selected.
4637         }
4638         if(!keepExisting){
4639             this.clearSelections(true);
4640         }
4641         
4642         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4643             Roo.fly(node).addClass(this.selectedClass);
4644             this.selections.push(node);
4645             if(!suppressEvent){
4646                 this.fireEvent("selectionchange", this, this.selections);
4647             }
4648         }
4649         
4650         
4651     },
4652       /**
4653      * Unselects nodes.
4654      * @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
4655      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4656      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4657      */
4658     unselect : function(nodeInfo, keepExisting, suppressEvent)
4659     {
4660         if(nodeInfo instanceof Array){
4661             Roo.each(this.selections, function(s) {
4662                 this.unselect(s, nodeInfo);
4663             }, this);
4664             return;
4665         }
4666         var node = this.getNode(nodeInfo);
4667         if(!node || !this.isSelected(node)){
4668             //Roo.log("not selected");
4669             return; // not selected.
4670         }
4671         // fireevent???
4672         var ns = [];
4673         Roo.each(this.selections, function(s) {
4674             if (s == node ) {
4675                 Roo.fly(node).removeClass(this.selectedClass);
4676
4677                 return;
4678             }
4679             ns.push(s);
4680         },this);
4681         
4682         this.selections= ns;
4683         this.fireEvent("selectionchange", this, this.selections);
4684     },
4685
4686     /**
4687      * Gets a template node.
4688      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4689      * @return {HTMLElement} The node or null if it wasn't found
4690      */
4691     getNode : function(nodeInfo){
4692         if(typeof nodeInfo == "string"){
4693             return document.getElementById(nodeInfo);
4694         }else if(typeof nodeInfo == "number"){
4695             return this.nodes[nodeInfo];
4696         }
4697         return nodeInfo;
4698     },
4699
4700     /**
4701      * Gets a range template nodes.
4702      * @param {Number} startIndex
4703      * @param {Number} endIndex
4704      * @return {Array} An array of nodes
4705      */
4706     getNodes : function(start, end){
4707         var ns = this.nodes;
4708         start = start || 0;
4709         end = typeof end == "undefined" ? ns.length - 1 : end;
4710         var nodes = [];
4711         if(start <= end){
4712             for(var i = start; i <= end; i++){
4713                 nodes.push(ns[i]);
4714             }
4715         } else{
4716             for(var i = start; i >= end; i--){
4717                 nodes.push(ns[i]);
4718             }
4719         }
4720         return nodes;
4721     },
4722
4723     /**
4724      * Finds the index of the passed node
4725      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4726      * @return {Number} The index of the node or -1
4727      */
4728     indexOf : function(node){
4729         node = this.getNode(node);
4730         if(typeof node.nodeIndex == "number"){
4731             return node.nodeIndex;
4732         }
4733         var ns = this.nodes;
4734         for(var i = 0, len = ns.length; i < len; i++){
4735             if(ns[i] == node){
4736                 return i;
4737             }
4738         }
4739         return -1;
4740     }
4741 });
4742 /*
4743  * Based on:
4744  * Ext JS Library 1.1.1
4745  * Copyright(c) 2006-2007, Ext JS, LLC.
4746  *
4747  * Originally Released Under LGPL - original licence link has changed is not relivant.
4748  *
4749  * Fork - LGPL
4750  * <script type="text/javascript">
4751  */
4752
4753 /**
4754  * @class Roo.JsonView
4755  * @extends Roo.View
4756  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4757 <pre><code>
4758 var view = new Roo.JsonView({
4759     container: "my-element",
4760     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4761     multiSelect: true, 
4762     jsonRoot: "data" 
4763 });
4764
4765 // listen for node click?
4766 view.on("click", function(vw, index, node, e){
4767     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4768 });
4769
4770 // direct load of JSON data
4771 view.load("foobar.php");
4772
4773 // Example from my blog list
4774 var tpl = new Roo.Template(
4775     '&lt;div class="entry"&gt;' +
4776     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4777     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4778     "&lt;/div&gt;&lt;hr /&gt;"
4779 );
4780
4781 var moreView = new Roo.JsonView({
4782     container :  "entry-list", 
4783     template : tpl,
4784     jsonRoot: "posts"
4785 });
4786 moreView.on("beforerender", this.sortEntries, this);
4787 moreView.load({
4788     url: "/blog/get-posts.php",
4789     params: "allposts=true",
4790     text: "Loading Blog Entries..."
4791 });
4792 </code></pre>
4793
4794 * Note: old code is supported with arguments : (container, template, config)
4795
4796
4797  * @constructor
4798  * Create a new JsonView
4799  * 
4800  * @param {Object} config The config object
4801  * 
4802  */
4803 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4804     
4805     
4806     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4807
4808     var um = this.el.getUpdateManager();
4809     um.setRenderer(this);
4810     um.on("update", this.onLoad, this);
4811     um.on("failure", this.onLoadException, this);
4812
4813     /**
4814      * @event beforerender
4815      * Fires before rendering of the downloaded JSON data.
4816      * @param {Roo.JsonView} this
4817      * @param {Object} data The JSON data loaded
4818      */
4819     /**
4820      * @event load
4821      * Fires when data is loaded.
4822      * @param {Roo.JsonView} this
4823      * @param {Object} data The JSON data loaded
4824      * @param {Object} response The raw Connect response object
4825      */
4826     /**
4827      * @event loadexception
4828      * Fires when loading fails.
4829      * @param {Roo.JsonView} this
4830      * @param {Object} response The raw Connect response object
4831      */
4832     this.addEvents({
4833         'beforerender' : true,
4834         'load' : true,
4835         'loadexception' : true
4836     });
4837 };
4838 Roo.extend(Roo.JsonView, Roo.View, {
4839     /**
4840      * @type {String} The root property in the loaded JSON object that contains the data
4841      */
4842     jsonRoot : "",
4843
4844     /**
4845      * Refreshes the view.
4846      */
4847     refresh : function(){
4848         this.clearSelections();
4849         this.el.update("");
4850         var html = [];
4851         var o = this.jsonData;
4852         if(o && o.length > 0){
4853             for(var i = 0, len = o.length; i < len; i++){
4854                 var data = this.prepareData(o[i], i, o);
4855                 html[html.length] = this.tpl.apply(data);
4856             }
4857         }else{
4858             html.push(this.emptyText);
4859         }
4860         this.el.update(html.join(""));
4861         this.nodes = this.el.dom.childNodes;
4862         this.updateIndexes(0);
4863     },
4864
4865     /**
4866      * 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.
4867      * @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:
4868      <pre><code>
4869      view.load({
4870          url: "your-url.php",
4871          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4872          callback: yourFunction,
4873          scope: yourObject, //(optional scope)
4874          discardUrl: false,
4875          nocache: false,
4876          text: "Loading...",
4877          timeout: 30,
4878          scripts: false
4879      });
4880      </code></pre>
4881      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4882      * 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.
4883      * @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}
4884      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4885      * @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.
4886      */
4887     load : function(){
4888         var um = this.el.getUpdateManager();
4889         um.update.apply(um, arguments);
4890     },
4891
4892     // note - render is a standard framework call...
4893     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4894     render : function(el, response){
4895         
4896         this.clearSelections();
4897         this.el.update("");
4898         var o;
4899         try{
4900             if (response != '') {
4901                 o = Roo.util.JSON.decode(response.responseText);
4902                 if(this.jsonRoot){
4903                     
4904                     o = o[this.jsonRoot];
4905                 }
4906             }
4907         } catch(e){
4908         }
4909         /**
4910          * The current JSON data or null
4911          */
4912         this.jsonData = o;
4913         this.beforeRender();
4914         this.refresh();
4915     },
4916
4917 /**
4918  * Get the number of records in the current JSON dataset
4919  * @return {Number}
4920  */
4921     getCount : function(){
4922         return this.jsonData ? this.jsonData.length : 0;
4923     },
4924
4925 /**
4926  * Returns the JSON object for the specified node(s)
4927  * @param {HTMLElement/Array} node The node or an array of nodes
4928  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4929  * you get the JSON object for the node
4930  */
4931     getNodeData : function(node){
4932         if(node instanceof Array){
4933             var data = [];
4934             for(var i = 0, len = node.length; i < len; i++){
4935                 data.push(this.getNodeData(node[i]));
4936             }
4937             return data;
4938         }
4939         return this.jsonData[this.indexOf(node)] || null;
4940     },
4941
4942     beforeRender : function(){
4943         this.snapshot = this.jsonData;
4944         if(this.sortInfo){
4945             this.sort.apply(this, this.sortInfo);
4946         }
4947         this.fireEvent("beforerender", this, this.jsonData);
4948     },
4949
4950     onLoad : function(el, o){
4951         this.fireEvent("load", this, this.jsonData, o);
4952     },
4953
4954     onLoadException : function(el, o){
4955         this.fireEvent("loadexception", this, o);
4956     },
4957
4958 /**
4959  * Filter the data by a specific property.
4960  * @param {String} property A property on your JSON objects
4961  * @param {String/RegExp} value Either string that the property values
4962  * should start with, or a RegExp to test against the property
4963  */
4964     filter : function(property, value){
4965         if(this.jsonData){
4966             var data = [];
4967             var ss = this.snapshot;
4968             if(typeof value == "string"){
4969                 var vlen = value.length;
4970                 if(vlen == 0){
4971                     this.clearFilter();
4972                     return;
4973                 }
4974                 value = value.toLowerCase();
4975                 for(var i = 0, len = ss.length; i < len; i++){
4976                     var o = ss[i];
4977                     if(o[property].substr(0, vlen).toLowerCase() == value){
4978                         data.push(o);
4979                     }
4980                 }
4981             } else if(value.exec){ // regex?
4982                 for(var i = 0, len = ss.length; i < len; i++){
4983                     var o = ss[i];
4984                     if(value.test(o[property])){
4985                         data.push(o);
4986                     }
4987                 }
4988             } else{
4989                 return;
4990             }
4991             this.jsonData = data;
4992             this.refresh();
4993         }
4994     },
4995
4996 /**
4997  * Filter by a function. The passed function will be called with each
4998  * object in the current dataset. If the function returns true the value is kept,
4999  * otherwise it is filtered.
5000  * @param {Function} fn
5001  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
5002  */
5003     filterBy : function(fn, scope){
5004         if(this.jsonData){
5005             var data = [];
5006             var ss = this.snapshot;
5007             for(var i = 0, len = ss.length; i < len; i++){
5008                 var o = ss[i];
5009                 if(fn.call(scope || this, o)){
5010                     data.push(o);
5011                 }
5012             }
5013             this.jsonData = data;
5014             this.refresh();
5015         }
5016     },
5017
5018 /**
5019  * Clears the current filter.
5020  */
5021     clearFilter : function(){
5022         if(this.snapshot && this.jsonData != this.snapshot){
5023             this.jsonData = this.snapshot;
5024             this.refresh();
5025         }
5026     },
5027
5028
5029 /**
5030  * Sorts the data for this view and refreshes it.
5031  * @param {String} property A property on your JSON objects to sort on
5032  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
5033  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
5034  */
5035     sort : function(property, dir, sortType){
5036         this.sortInfo = Array.prototype.slice.call(arguments, 0);
5037         if(this.jsonData){
5038             var p = property;
5039             var dsc = dir && dir.toLowerCase() == "desc";
5040             var f = function(o1, o2){
5041                 var v1 = sortType ? sortType(o1[p]) : o1[p];
5042                 var v2 = sortType ? sortType(o2[p]) : o2[p];
5043                 ;
5044                 if(v1 < v2){
5045                     return dsc ? +1 : -1;
5046                 } else if(v1 > v2){
5047                     return dsc ? -1 : +1;
5048                 } else{
5049                     return 0;
5050                 }
5051             };
5052             this.jsonData.sort(f);
5053             this.refresh();
5054             if(this.jsonData != this.snapshot){
5055                 this.snapshot.sort(f);
5056             }
5057         }
5058     }
5059 });/*
5060  * Based on:
5061  * Ext JS Library 1.1.1
5062  * Copyright(c) 2006-2007, Ext JS, LLC.
5063  *
5064  * Originally Released Under LGPL - original licence link has changed is not relivant.
5065  *
5066  * Fork - LGPL
5067  * <script type="text/javascript">
5068  */
5069  
5070
5071 /**
5072  * @class Roo.ColorPalette
5073  * @extends Roo.Component
5074  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
5075  * Here's an example of typical usage:
5076  * <pre><code>
5077 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
5078 cp.render('my-div');
5079
5080 cp.on('select', function(palette, selColor){
5081     // do something with selColor
5082 });
5083 </code></pre>
5084  * @constructor
5085  * Create a new ColorPalette
5086  * @param {Object} config The config object
5087  */
5088 Roo.ColorPalette = function(config){
5089     Roo.ColorPalette.superclass.constructor.call(this, config);
5090     this.addEvents({
5091         /**
5092              * @event select
5093              * Fires when a color is selected
5094              * @param {ColorPalette} this
5095              * @param {String} color The 6-digit color hex code (without the # symbol)
5096              */
5097         select: true
5098     });
5099
5100     if(this.handler){
5101         this.on("select", this.handler, this.scope, true);
5102     }
5103 };
5104 Roo.extend(Roo.ColorPalette, Roo.Component, {
5105     /**
5106      * @cfg {String} itemCls
5107      * The CSS class to apply to the containing element (defaults to "x-color-palette")
5108      */
5109     itemCls : "x-color-palette",
5110     /**
5111      * @cfg {String} value
5112      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
5113      * the hex codes are case-sensitive.
5114      */
5115     value : null,
5116     clickEvent:'click',
5117     // private
5118     ctype: "Roo.ColorPalette",
5119
5120     /**
5121      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
5122      */
5123     allowReselect : false,
5124
5125     /**
5126      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
5127      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
5128      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
5129      * of colors with the width setting until the box is symmetrical.</p>
5130      * <p>You can override individual colors if needed:</p>
5131      * <pre><code>
5132 var cp = new Roo.ColorPalette();
5133 cp.colors[0] = "FF0000";  // change the first box to red
5134 </code></pre>
5135
5136 Or you can provide a custom array of your own for complete control:
5137 <pre><code>
5138 var cp = new Roo.ColorPalette();
5139 cp.colors = ["000000", "993300", "333300"];
5140 </code></pre>
5141      * @type Array
5142      */
5143     colors : [
5144         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
5145         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
5146         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
5147         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
5148         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
5149     ],
5150
5151     // private
5152     onRender : function(container, position){
5153         var t = new Roo.MasterTemplate(
5154             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
5155         );
5156         var c = this.colors;
5157         for(var i = 0, len = c.length; i < len; i++){
5158             t.add([c[i]]);
5159         }
5160         var el = document.createElement("div");
5161         el.className = this.itemCls;
5162         t.overwrite(el);
5163         container.dom.insertBefore(el, position);
5164         this.el = Roo.get(el);
5165         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
5166         if(this.clickEvent != 'click'){
5167             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
5168         }
5169     },
5170
5171     // private
5172     afterRender : function(){
5173         Roo.ColorPalette.superclass.afterRender.call(this);
5174         if(this.value){
5175             var s = this.value;
5176             this.value = null;
5177             this.select(s);
5178         }
5179     },
5180
5181     // private
5182     handleClick : function(e, t){
5183         e.preventDefault();
5184         if(!this.disabled){
5185             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
5186             this.select(c.toUpperCase());
5187         }
5188     },
5189
5190     /**
5191      * Selects the specified color in the palette (fires the select event)
5192      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
5193      */
5194     select : function(color){
5195         color = color.replace("#", "");
5196         if(color != this.value || this.allowReselect){
5197             var el = this.el;
5198             if(this.value){
5199                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
5200             }
5201             el.child("a.color-"+color).addClass("x-color-palette-sel");
5202             this.value = color;
5203             this.fireEvent("select", this, color);
5204         }
5205     }
5206 });/*
5207  * Based on:
5208  * Ext JS Library 1.1.1
5209  * Copyright(c) 2006-2007, Ext JS, LLC.
5210  *
5211  * Originally Released Under LGPL - original licence link has changed is not relivant.
5212  *
5213  * Fork - LGPL
5214  * <script type="text/javascript">
5215  */
5216  
5217 /**
5218  * @class Roo.DatePicker
5219  * @extends Roo.Component
5220  * Simple date picker class.
5221  * @constructor
5222  * Create a new DatePicker
5223  * @param {Object} config The config object
5224  */
5225 Roo.DatePicker = function(config){
5226     Roo.DatePicker.superclass.constructor.call(this, config);
5227
5228     this.value = config && config.value ?
5229                  config.value.clearTime() : new Date().clearTime();
5230
5231     this.addEvents({
5232         /**
5233              * @event select
5234              * Fires when a date is selected
5235              * @param {DatePicker} this
5236              * @param {Date} date The selected date
5237              */
5238         'select': true,
5239         /**
5240              * @event monthchange
5241              * Fires when the displayed month changes 
5242              * @param {DatePicker} this
5243              * @param {Date} date The selected month
5244              */
5245         'monthchange': true
5246     });
5247
5248     if(this.handler){
5249         this.on("select", this.handler,  this.scope || this);
5250     }
5251     // build the disabledDatesRE
5252     if(!this.disabledDatesRE && this.disabledDates){
5253         var dd = this.disabledDates;
5254         var re = "(?:";
5255         for(var i = 0; i < dd.length; i++){
5256             re += dd[i];
5257             if(i != dd.length-1) {
5258                 re += "|";
5259             }
5260         }
5261         this.disabledDatesRE = new RegExp(re + ")");
5262     }
5263 };
5264
5265 Roo.extend(Roo.DatePicker, Roo.Component, {
5266     /**
5267      * @cfg {String} todayText
5268      * The text to display on the button that selects the current date (defaults to "Today")
5269      */
5270     todayText : "Today",
5271     /**
5272      * @cfg {String} okText
5273      * The text to display on the ok button
5274      */
5275     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
5276     /**
5277      * @cfg {String} cancelText
5278      * The text to display on the cancel button
5279      */
5280     cancelText : "Cancel",
5281     /**
5282      * @cfg {String} todayTip
5283      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
5284      */
5285     todayTip : "{0} (Spacebar)",
5286     /**
5287      * @cfg {Date} minDate
5288      * Minimum allowable date (JavaScript date object, defaults to null)
5289      */
5290     minDate : null,
5291     /**
5292      * @cfg {Date} maxDate
5293      * Maximum allowable date (JavaScript date object, defaults to null)
5294      */
5295     maxDate : null,
5296     /**
5297      * @cfg {String} minText
5298      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
5299      */
5300     minText : "This date is before the minimum date",
5301     /**
5302      * @cfg {String} maxText
5303      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
5304      */
5305     maxText : "This date is after the maximum date",
5306     /**
5307      * @cfg {String} format
5308      * The default date format string which can be overriden for localization support.  The format must be
5309      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
5310      */
5311     format : "m/d/y",
5312     /**
5313      * @cfg {Array} disabledDays
5314      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
5315      */
5316     disabledDays : null,
5317     /**
5318      * @cfg {String} disabledDaysText
5319      * The tooltip to display when the date falls on a disabled day (defaults to "")
5320      */
5321     disabledDaysText : "",
5322     /**
5323      * @cfg {RegExp} disabledDatesRE
5324      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
5325      */
5326     disabledDatesRE : null,
5327     /**
5328      * @cfg {String} disabledDatesText
5329      * The tooltip text to display when the date falls on a disabled date (defaults to "")
5330      */
5331     disabledDatesText : "",
5332     /**
5333      * @cfg {Boolean} constrainToViewport
5334      * True to constrain the date picker to the viewport (defaults to true)
5335      */
5336     constrainToViewport : true,
5337     /**
5338      * @cfg {Array} monthNames
5339      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
5340      */
5341     monthNames : Date.monthNames,
5342     /**
5343      * @cfg {Array} dayNames
5344      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
5345      */
5346     dayNames : Date.dayNames,
5347     /**
5348      * @cfg {String} nextText
5349      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
5350      */
5351     nextText: 'Next Month (Control+Right)',
5352     /**
5353      * @cfg {String} prevText
5354      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
5355      */
5356     prevText: 'Previous Month (Control+Left)',
5357     /**
5358      * @cfg {String} monthYearText
5359      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
5360      */
5361     monthYearText: 'Choose a month (Control+Up/Down to move years)',
5362     /**
5363      * @cfg {Number} startDay
5364      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
5365      */
5366     startDay : 0,
5367     /**
5368      * @cfg {Bool} showClear
5369      * Show a clear button (usefull for date form elements that can be blank.)
5370      */
5371     
5372     showClear: false,
5373     
5374     /**
5375      * Sets the value of the date field
5376      * @param {Date} value The date to set
5377      */
5378     setValue : function(value){
5379         var old = this.value;
5380         
5381         if (typeof(value) == 'string') {
5382          
5383             value = Date.parseDate(value, this.format);
5384         }
5385         if (!value) {
5386             value = new Date();
5387         }
5388         
5389         this.value = value.clearTime(true);
5390         if(this.el){
5391             this.update(this.value);
5392         }
5393     },
5394
5395     /**
5396      * Gets the current selected value of the date field
5397      * @return {Date} The selected date
5398      */
5399     getValue : function(){
5400         return this.value;
5401     },
5402
5403     // private
5404     focus : function(){
5405         if(this.el){
5406             this.update(this.activeDate);
5407         }
5408     },
5409
5410     // privateval
5411     onRender : function(container, position){
5412         
5413         var m = [
5414              '<table cellspacing="0">',
5415                 '<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>',
5416                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
5417         var dn = this.dayNames;
5418         for(var i = 0; i < 7; i++){
5419             var d = this.startDay+i;
5420             if(d > 6){
5421                 d = d-7;
5422             }
5423             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
5424         }
5425         m[m.length] = "</tr></thead><tbody><tr>";
5426         for(var i = 0; i < 42; i++) {
5427             if(i % 7 == 0 && i != 0){
5428                 m[m.length] = "</tr><tr>";
5429             }
5430             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5431         }
5432         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5433             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5434
5435         var el = document.createElement("div");
5436         el.className = "x-date-picker";
5437         el.innerHTML = m.join("");
5438
5439         container.dom.insertBefore(el, position);
5440
5441         this.el = Roo.get(el);
5442         this.eventEl = Roo.get(el.firstChild);
5443
5444         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5445             handler: this.showPrevMonth,
5446             scope: this,
5447             preventDefault:true,
5448             stopDefault:true
5449         });
5450
5451         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5452             handler: this.showNextMonth,
5453             scope: this,
5454             preventDefault:true,
5455             stopDefault:true
5456         });
5457
5458         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5459
5460         this.monthPicker = this.el.down('div.x-date-mp');
5461         this.monthPicker.enableDisplayMode('block');
5462         
5463         var kn = new Roo.KeyNav(this.eventEl, {
5464             "left" : function(e){
5465                 e.ctrlKey ?
5466                     this.showPrevMonth() :
5467                     this.update(this.activeDate.add("d", -1));
5468             },
5469
5470             "right" : function(e){
5471                 e.ctrlKey ?
5472                     this.showNextMonth() :
5473                     this.update(this.activeDate.add("d", 1));
5474             },
5475
5476             "up" : function(e){
5477                 e.ctrlKey ?
5478                     this.showNextYear() :
5479                     this.update(this.activeDate.add("d", -7));
5480             },
5481
5482             "down" : function(e){
5483                 e.ctrlKey ?
5484                     this.showPrevYear() :
5485                     this.update(this.activeDate.add("d", 7));
5486             },
5487
5488             "pageUp" : function(e){
5489                 this.showNextMonth();
5490             },
5491
5492             "pageDown" : function(e){
5493                 this.showPrevMonth();
5494             },
5495
5496             "enter" : function(e){
5497                 e.stopPropagation();
5498                 return true;
5499             },
5500
5501             scope : this
5502         });
5503
5504         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5505
5506         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5507
5508         this.el.unselectable();
5509         
5510         this.cells = this.el.select("table.x-date-inner tbody td");
5511         this.textNodes = this.el.query("table.x-date-inner tbody span");
5512
5513         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5514             text: "&#160;",
5515             tooltip: this.monthYearText
5516         });
5517
5518         this.mbtn.on('click', this.showMonthPicker, this);
5519         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5520
5521
5522         var today = (new Date()).dateFormat(this.format);
5523         
5524         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5525         if (this.showClear) {
5526             baseTb.add( new Roo.Toolbar.Fill());
5527         }
5528         baseTb.add({
5529             text: String.format(this.todayText, today),
5530             tooltip: String.format(this.todayTip, today),
5531             handler: this.selectToday,
5532             scope: this
5533         });
5534         
5535         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5536             
5537         //});
5538         if (this.showClear) {
5539             
5540             baseTb.add( new Roo.Toolbar.Fill());
5541             baseTb.add({
5542                 text: '&#160;',
5543                 cls: 'x-btn-icon x-btn-clear',
5544                 handler: function() {
5545                     //this.value = '';
5546                     this.fireEvent("select", this, '');
5547                 },
5548                 scope: this
5549             });
5550         }
5551         
5552         
5553         if(Roo.isIE){
5554             this.el.repaint();
5555         }
5556         this.update(this.value);
5557     },
5558
5559     createMonthPicker : function(){
5560         if(!this.monthPicker.dom.firstChild){
5561             var buf = ['<table border="0" cellspacing="0">'];
5562             for(var i = 0; i < 6; i++){
5563                 buf.push(
5564                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5565                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5566                     i == 0 ?
5567                     '<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>' :
5568                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5569                 );
5570             }
5571             buf.push(
5572                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5573                     this.okText,
5574                     '</button><button type="button" class="x-date-mp-cancel">',
5575                     this.cancelText,
5576                     '</button></td></tr>',
5577                 '</table>'
5578             );
5579             this.monthPicker.update(buf.join(''));
5580             this.monthPicker.on('click', this.onMonthClick, this);
5581             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5582
5583             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5584             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5585
5586             this.mpMonths.each(function(m, a, i){
5587                 i += 1;
5588                 if((i%2) == 0){
5589                     m.dom.xmonth = 5 + Math.round(i * .5);
5590                 }else{
5591                     m.dom.xmonth = Math.round((i-1) * .5);
5592                 }
5593             });
5594         }
5595     },
5596
5597     showMonthPicker : function(){
5598         this.createMonthPicker();
5599         var size = this.el.getSize();
5600         this.monthPicker.setSize(size);
5601         this.monthPicker.child('table').setSize(size);
5602
5603         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5604         this.updateMPMonth(this.mpSelMonth);
5605         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5606         this.updateMPYear(this.mpSelYear);
5607
5608         this.monthPicker.slideIn('t', {duration:.2});
5609     },
5610
5611     updateMPYear : function(y){
5612         this.mpyear = y;
5613         var ys = this.mpYears.elements;
5614         for(var i = 1; i <= 10; i++){
5615             var td = ys[i-1], y2;
5616             if((i%2) == 0){
5617                 y2 = y + Math.round(i * .5);
5618                 td.firstChild.innerHTML = y2;
5619                 td.xyear = y2;
5620             }else{
5621                 y2 = y - (5-Math.round(i * .5));
5622                 td.firstChild.innerHTML = y2;
5623                 td.xyear = y2;
5624             }
5625             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5626         }
5627     },
5628
5629     updateMPMonth : function(sm){
5630         this.mpMonths.each(function(m, a, i){
5631             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5632         });
5633     },
5634
5635     selectMPMonth: function(m){
5636         
5637     },
5638
5639     onMonthClick : function(e, t){
5640         e.stopEvent();
5641         var el = new Roo.Element(t), pn;
5642         if(el.is('button.x-date-mp-cancel')){
5643             this.hideMonthPicker();
5644         }
5645         else if(el.is('button.x-date-mp-ok')){
5646             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5647             this.hideMonthPicker();
5648         }
5649         else if(pn = el.up('td.x-date-mp-month', 2)){
5650             this.mpMonths.removeClass('x-date-mp-sel');
5651             pn.addClass('x-date-mp-sel');
5652             this.mpSelMonth = pn.dom.xmonth;
5653         }
5654         else if(pn = el.up('td.x-date-mp-year', 2)){
5655             this.mpYears.removeClass('x-date-mp-sel');
5656             pn.addClass('x-date-mp-sel');
5657             this.mpSelYear = pn.dom.xyear;
5658         }
5659         else if(el.is('a.x-date-mp-prev')){
5660             this.updateMPYear(this.mpyear-10);
5661         }
5662         else if(el.is('a.x-date-mp-next')){
5663             this.updateMPYear(this.mpyear+10);
5664         }
5665     },
5666
5667     onMonthDblClick : function(e, t){
5668         e.stopEvent();
5669         var el = new Roo.Element(t), pn;
5670         if(pn = el.up('td.x-date-mp-month', 2)){
5671             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5672             this.hideMonthPicker();
5673         }
5674         else if(pn = el.up('td.x-date-mp-year', 2)){
5675             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5676             this.hideMonthPicker();
5677         }
5678     },
5679
5680     hideMonthPicker : function(disableAnim){
5681         if(this.monthPicker){
5682             if(disableAnim === true){
5683                 this.monthPicker.hide();
5684             }else{
5685                 this.monthPicker.slideOut('t', {duration:.2});
5686             }
5687         }
5688     },
5689
5690     // private
5691     showPrevMonth : function(e){
5692         this.update(this.activeDate.add("mo", -1));
5693     },
5694
5695     // private
5696     showNextMonth : function(e){
5697         this.update(this.activeDate.add("mo", 1));
5698     },
5699
5700     // private
5701     showPrevYear : function(){
5702         this.update(this.activeDate.add("y", -1));
5703     },
5704
5705     // private
5706     showNextYear : function(){
5707         this.update(this.activeDate.add("y", 1));
5708     },
5709
5710     // private
5711     handleMouseWheel : function(e){
5712         var delta = e.getWheelDelta();
5713         if(delta > 0){
5714             this.showPrevMonth();
5715             e.stopEvent();
5716         } else if(delta < 0){
5717             this.showNextMonth();
5718             e.stopEvent();
5719         }
5720     },
5721
5722     // private
5723     handleDateClick : function(e, t){
5724         e.stopEvent();
5725         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5726             this.setValue(new Date(t.dateValue));
5727             this.fireEvent("select", this, this.value);
5728         }
5729     },
5730
5731     // private
5732     selectToday : function(){
5733         this.setValue(new Date().clearTime());
5734         this.fireEvent("select", this, this.value);
5735     },
5736
5737     // private
5738     update : function(date)
5739     {
5740         var vd = this.activeDate;
5741         this.activeDate = date;
5742         if(vd && this.el){
5743             var t = date.getTime();
5744             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5745                 this.cells.removeClass("x-date-selected");
5746                 this.cells.each(function(c){
5747                    if(c.dom.firstChild.dateValue == t){
5748                        c.addClass("x-date-selected");
5749                        setTimeout(function(){
5750                             try{c.dom.firstChild.focus();}catch(e){}
5751                        }, 50);
5752                        return false;
5753                    }
5754                 });
5755                 return;
5756             }
5757         }
5758         
5759         var days = date.getDaysInMonth();
5760         var firstOfMonth = date.getFirstDateOfMonth();
5761         var startingPos = firstOfMonth.getDay()-this.startDay;
5762
5763         if(startingPos <= this.startDay){
5764             startingPos += 7;
5765         }
5766
5767         var pm = date.add("mo", -1);
5768         var prevStart = pm.getDaysInMonth()-startingPos;
5769
5770         var cells = this.cells.elements;
5771         var textEls = this.textNodes;
5772         days += startingPos;
5773
5774         // convert everything to numbers so it's fast
5775         var day = 86400000;
5776         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5777         var today = new Date().clearTime().getTime();
5778         var sel = date.clearTime().getTime();
5779         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5780         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5781         var ddMatch = this.disabledDatesRE;
5782         var ddText = this.disabledDatesText;
5783         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5784         var ddaysText = this.disabledDaysText;
5785         var format = this.format;
5786
5787         var setCellClass = function(cal, cell){
5788             cell.title = "";
5789             var t = d.getTime();
5790             cell.firstChild.dateValue = t;
5791             if(t == today){
5792                 cell.className += " x-date-today";
5793                 cell.title = cal.todayText;
5794             }
5795             if(t == sel){
5796                 cell.className += " x-date-selected";
5797                 setTimeout(function(){
5798                     try{cell.firstChild.focus();}catch(e){}
5799                 }, 50);
5800             }
5801             // disabling
5802             if(t < min) {
5803                 cell.className = " x-date-disabled";
5804                 cell.title = cal.minText;
5805                 return;
5806             }
5807             if(t > max) {
5808                 cell.className = " x-date-disabled";
5809                 cell.title = cal.maxText;
5810                 return;
5811             }
5812             if(ddays){
5813                 if(ddays.indexOf(d.getDay()) != -1){
5814                     cell.title = ddaysText;
5815                     cell.className = " x-date-disabled";
5816                 }
5817             }
5818             if(ddMatch && format){
5819                 var fvalue = d.dateFormat(format);
5820                 if(ddMatch.test(fvalue)){
5821                     cell.title = ddText.replace("%0", fvalue);
5822                     cell.className = " x-date-disabled";
5823                 }
5824             }
5825         };
5826
5827         var i = 0;
5828         for(; i < startingPos; i++) {
5829             textEls[i].innerHTML = (++prevStart);
5830             d.setDate(d.getDate()+1);
5831             cells[i].className = "x-date-prevday";
5832             setCellClass(this, cells[i]);
5833         }
5834         for(; i < days; i++){
5835             intDay = i - startingPos + 1;
5836             textEls[i].innerHTML = (intDay);
5837             d.setDate(d.getDate()+1);
5838             cells[i].className = "x-date-active";
5839             setCellClass(this, cells[i]);
5840         }
5841         var extraDays = 0;
5842         for(; i < 42; i++) {
5843              textEls[i].innerHTML = (++extraDays);
5844              d.setDate(d.getDate()+1);
5845              cells[i].className = "x-date-nextday";
5846              setCellClass(this, cells[i]);
5847         }
5848
5849         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5850         this.fireEvent('monthchange', this, date);
5851         
5852         if(!this.internalRender){
5853             var main = this.el.dom.firstChild;
5854             var w = main.offsetWidth;
5855             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5856             Roo.fly(main).setWidth(w);
5857             this.internalRender = true;
5858             // opera does not respect the auto grow header center column
5859             // then, after it gets a width opera refuses to recalculate
5860             // without a second pass
5861             if(Roo.isOpera && !this.secondPass){
5862                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5863                 this.secondPass = true;
5864                 this.update.defer(10, this, [date]);
5865             }
5866         }
5867         
5868         
5869     }
5870 });        /*
5871  * Based on:
5872  * Ext JS Library 1.1.1
5873  * Copyright(c) 2006-2007, Ext JS, LLC.
5874  *
5875  * Originally Released Under LGPL - original licence link has changed is not relivant.
5876  *
5877  * Fork - LGPL
5878  * <script type="text/javascript">
5879  */
5880 /**
5881  * @class Roo.TabPanel
5882  * @extends Roo.util.Observable
5883  * A lightweight tab container.
5884  * <br><br>
5885  * Usage:
5886  * <pre><code>
5887 // basic tabs 1, built from existing content
5888 var tabs = new Roo.TabPanel("tabs1");
5889 tabs.addTab("script", "View Script");
5890 tabs.addTab("markup", "View Markup");
5891 tabs.activate("script");
5892
5893 // more advanced tabs, built from javascript
5894 var jtabs = new Roo.TabPanel("jtabs");
5895 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5896
5897 // set up the UpdateManager
5898 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5899 var updater = tab2.getUpdateManager();
5900 updater.setDefaultUrl("ajax1.htm");
5901 tab2.on('activate', updater.refresh, updater, true);
5902
5903 // Use setUrl for Ajax loading
5904 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5905 tab3.setUrl("ajax2.htm", null, true);
5906
5907 // Disabled tab
5908 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5909 tab4.disable();
5910
5911 jtabs.activate("jtabs-1");
5912  * </code></pre>
5913  * @constructor
5914  * Create a new TabPanel.
5915  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5916  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5917  */
5918 Roo.TabPanel = function(container, config){
5919     /**
5920     * The container element for this TabPanel.
5921     * @type Roo.Element
5922     */
5923     this.el = Roo.get(container, true);
5924     if(config){
5925         if(typeof config == "boolean"){
5926             this.tabPosition = config ? "bottom" : "top";
5927         }else{
5928             Roo.apply(this, config);
5929         }
5930     }
5931     if(this.tabPosition == "bottom"){
5932         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5933         this.el.addClass("x-tabs-bottom");
5934     }
5935     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5936     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5937     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5938     if(Roo.isIE){
5939         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5940     }
5941     if(this.tabPosition != "bottom"){
5942         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5943          * @type Roo.Element
5944          */
5945         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5946         this.el.addClass("x-tabs-top");
5947     }
5948     this.items = [];
5949
5950     this.bodyEl.setStyle("position", "relative");
5951
5952     this.active = null;
5953     this.activateDelegate = this.activate.createDelegate(this);
5954
5955     this.addEvents({
5956         /**
5957          * @event tabchange
5958          * Fires when the active tab changes
5959          * @param {Roo.TabPanel} this
5960          * @param {Roo.TabPanelItem} activePanel The new active tab
5961          */
5962         "tabchange": true,
5963         /**
5964          * @event beforetabchange
5965          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5966          * @param {Roo.TabPanel} this
5967          * @param {Object} e Set cancel to true on this object to cancel the tab change
5968          * @param {Roo.TabPanelItem} tab The tab being changed to
5969          */
5970         "beforetabchange" : true
5971     });
5972
5973     Roo.EventManager.onWindowResize(this.onResize, this);
5974     this.cpad = this.el.getPadding("lr");
5975     this.hiddenCount = 0;
5976
5977
5978     // toolbar on the tabbar support...
5979     if (this.toolbar) {
5980         var tcfg = this.toolbar;
5981         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5982         this.toolbar = new Roo.Toolbar(tcfg);
5983         if (Roo.isSafari) {
5984             var tbl = tcfg.container.child('table', true);
5985             tbl.setAttribute('width', '100%');
5986         }
5987         
5988     }
5989    
5990
5991
5992     Roo.TabPanel.superclass.constructor.call(this);
5993 };
5994
5995 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5996     /*
5997      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5998      */
5999     tabPosition : "top",
6000     /*
6001      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
6002      */
6003     currentTabWidth : 0,
6004     /*
6005      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
6006      */
6007     minTabWidth : 40,
6008     /*
6009      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
6010      */
6011     maxTabWidth : 250,
6012     /*
6013      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
6014      */
6015     preferredTabWidth : 175,
6016     /*
6017      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
6018      */
6019     resizeTabs : false,
6020     /*
6021      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
6022      */
6023     monitorResize : true,
6024     /*
6025      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
6026      */
6027     toolbar : false,
6028
6029     /**
6030      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
6031      * @param {String} id The id of the div to use <b>or create</b>
6032      * @param {String} text The text for the tab
6033      * @param {String} content (optional) Content to put in the TabPanelItem body
6034      * @param {Boolean} closable (optional) True to create a close icon on the tab
6035      * @return {Roo.TabPanelItem} The created TabPanelItem
6036      */
6037     addTab : function(id, text, content, closable){
6038         var item = new Roo.TabPanelItem(this, id, text, closable);
6039         this.addTabItem(item);
6040         if(content){
6041             item.setContent(content);
6042         }
6043         return item;
6044     },
6045
6046     /**
6047      * Returns the {@link Roo.TabPanelItem} with the specified id/index
6048      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
6049      * @return {Roo.TabPanelItem}
6050      */
6051     getTab : function(id){
6052         return this.items[id];
6053     },
6054
6055     /**
6056      * Hides the {@link Roo.TabPanelItem} with the specified id/index
6057      * @param {String/Number} id The id or index of the TabPanelItem to hide.
6058      */
6059     hideTab : function(id){
6060         var t = this.items[id];
6061         if(!t.isHidden()){
6062            t.setHidden(true);
6063            this.hiddenCount++;
6064            this.autoSizeTabs();
6065         }
6066     },
6067
6068     /**
6069      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
6070      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
6071      */
6072     unhideTab : function(id){
6073         var t = this.items[id];
6074         if(t.isHidden()){
6075            t.setHidden(false);
6076            this.hiddenCount--;
6077            this.autoSizeTabs();
6078         }
6079     },
6080
6081     /**
6082      * Adds an existing {@link Roo.TabPanelItem}.
6083      * @param {Roo.TabPanelItem} item The TabPanelItem to add
6084      */
6085     addTabItem : function(item){
6086         this.items[item.id] = item;
6087         this.items.push(item);
6088         if(this.resizeTabs){
6089            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
6090            this.autoSizeTabs();
6091         }else{
6092             item.autoSize();
6093         }
6094     },
6095
6096     /**
6097      * Removes a {@link Roo.TabPanelItem}.
6098      * @param {String/Number} id The id or index of the TabPanelItem to remove.
6099      */
6100     removeTab : function(id){
6101         var items = this.items;
6102         var tab = items[id];
6103         if(!tab) { return; }
6104         var index = items.indexOf(tab);
6105         if(this.active == tab && items.length > 1){
6106             var newTab = this.getNextAvailable(index);
6107             if(newTab) {
6108                 newTab.activate();
6109             }
6110         }
6111         this.stripEl.dom.removeChild(tab.pnode.dom);
6112         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
6113             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
6114         }
6115         items.splice(index, 1);
6116         delete this.items[tab.id];
6117         tab.fireEvent("close", tab);
6118         tab.purgeListeners();
6119         this.autoSizeTabs();
6120     },
6121
6122     getNextAvailable : function(start){
6123         var items = this.items;
6124         var index = start;
6125         // look for a next tab that will slide over to
6126         // replace the one being removed
6127         while(index < items.length){
6128             var item = items[++index];
6129             if(item && !item.isHidden()){
6130                 return item;
6131             }
6132         }
6133         // if one isn't found select the previous tab (on the left)
6134         index = start;
6135         while(index >= 0){
6136             var item = items[--index];
6137             if(item && !item.isHidden()){
6138                 return item;
6139             }
6140         }
6141         return null;
6142     },
6143
6144     /**
6145      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
6146      * @param {String/Number} id The id or index of the TabPanelItem to disable.
6147      */
6148     disableTab : function(id){
6149         var tab = this.items[id];
6150         if(tab && this.active != tab){
6151             tab.disable();
6152         }
6153     },
6154
6155     /**
6156      * Enables a {@link Roo.TabPanelItem} that is disabled.
6157      * @param {String/Number} id The id or index of the TabPanelItem to enable.
6158      */
6159     enableTab : function(id){
6160         var tab = this.items[id];
6161         tab.enable();
6162     },
6163
6164     /**
6165      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
6166      * @param {String/Number} id The id or index of the TabPanelItem to activate.
6167      * @return {Roo.TabPanelItem} The TabPanelItem.
6168      */
6169     activate : function(id){
6170         var tab = this.items[id];
6171         if(!tab){
6172             return null;
6173         }
6174         if(tab == this.active || tab.disabled){
6175             return tab;
6176         }
6177         var e = {};
6178         this.fireEvent("beforetabchange", this, e, tab);
6179         if(e.cancel !== true && !tab.disabled){
6180             if(this.active){
6181                 this.active.hide();
6182             }
6183             this.active = this.items[id];
6184             this.active.show();
6185             this.fireEvent("tabchange", this, this.active);
6186         }
6187         return tab;
6188     },
6189
6190     /**
6191      * Gets the active {@link Roo.TabPanelItem}.
6192      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
6193      */
6194     getActiveTab : function(){
6195         return this.active;
6196     },
6197
6198     /**
6199      * Updates the tab body element to fit the height of the container element
6200      * for overflow scrolling
6201      * @param {Number} targetHeight (optional) Override the starting height from the elements height
6202      */
6203     syncHeight : function(targetHeight){
6204         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
6205         var bm = this.bodyEl.getMargins();
6206         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
6207         this.bodyEl.setHeight(newHeight);
6208         return newHeight;
6209     },
6210
6211     onResize : function(){
6212         if(this.monitorResize){
6213             this.autoSizeTabs();
6214         }
6215     },
6216
6217     /**
6218      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
6219      */
6220     beginUpdate : function(){
6221         this.updating = true;
6222     },
6223
6224     /**
6225      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
6226      */
6227     endUpdate : function(){
6228         this.updating = false;
6229         this.autoSizeTabs();
6230     },
6231
6232     /**
6233      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
6234      */
6235     autoSizeTabs : function(){
6236         var count = this.items.length;
6237         var vcount = count - this.hiddenCount;
6238         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
6239             return;
6240         }
6241         var w = Math.max(this.el.getWidth() - this.cpad, 10);
6242         var availWidth = Math.floor(w / vcount);
6243         var b = this.stripBody;
6244         if(b.getWidth() > w){
6245             var tabs = this.items;
6246             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
6247             if(availWidth < this.minTabWidth){
6248                 /*if(!this.sleft){    // incomplete scrolling code
6249                     this.createScrollButtons();
6250                 }
6251                 this.showScroll();
6252                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
6253             }
6254         }else{
6255             if(this.currentTabWidth < this.preferredTabWidth){
6256                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
6257             }
6258         }
6259     },
6260
6261     /**
6262      * Returns the number of tabs in this TabPanel.
6263      * @return {Number}
6264      */
6265      getCount : function(){
6266          return this.items.length;
6267      },
6268
6269     /**
6270      * Resizes all the tabs to the passed width
6271      * @param {Number} The new width
6272      */
6273     setTabWidth : function(width){
6274         this.currentTabWidth = width;
6275         for(var i = 0, len = this.items.length; i < len; i++) {
6276                 if(!this.items[i].isHidden()) {
6277                 this.items[i].setWidth(width);
6278             }
6279         }
6280     },
6281
6282     /**
6283      * Destroys this TabPanel
6284      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
6285      */
6286     destroy : function(removeEl){
6287         Roo.EventManager.removeResizeListener(this.onResize, this);
6288         for(var i = 0, len = this.items.length; i < len; i++){
6289             this.items[i].purgeListeners();
6290         }
6291         if(removeEl === true){
6292             this.el.update("");
6293             this.el.remove();
6294         }
6295     }
6296 });
6297
6298 /**
6299  * @class Roo.TabPanelItem
6300  * @extends Roo.util.Observable
6301  * Represents an individual item (tab plus body) in a TabPanel.
6302  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
6303  * @param {String} id The id of this TabPanelItem
6304  * @param {String} text The text for the tab of this TabPanelItem
6305  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
6306  */
6307 Roo.TabPanelItem = function(tabPanel, id, text, closable){
6308     /**
6309      * The {@link Roo.TabPanel} this TabPanelItem belongs to
6310      * @type Roo.TabPanel
6311      */
6312     this.tabPanel = tabPanel;
6313     /**
6314      * The id for this TabPanelItem
6315      * @type String
6316      */
6317     this.id = id;
6318     /** @private */
6319     this.disabled = false;
6320     /** @private */
6321     this.text = text;
6322     /** @private */
6323     this.loaded = false;
6324     this.closable = closable;
6325
6326     /**
6327      * The body element for this TabPanelItem.
6328      * @type Roo.Element
6329      */
6330     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
6331     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
6332     this.bodyEl.setStyle("display", "block");
6333     this.bodyEl.setStyle("zoom", "1");
6334     this.hideAction();
6335
6336     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
6337     /** @private */
6338     this.el = Roo.get(els.el, true);
6339     this.inner = Roo.get(els.inner, true);
6340     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
6341     this.pnode = Roo.get(els.el.parentNode, true);
6342     this.el.on("mousedown", this.onTabMouseDown, this);
6343     this.el.on("click", this.onTabClick, this);
6344     /** @private */
6345     if(closable){
6346         var c = Roo.get(els.close, true);
6347         c.dom.title = this.closeText;
6348         c.addClassOnOver("close-over");
6349         c.on("click", this.closeClick, this);
6350      }
6351
6352     this.addEvents({
6353          /**
6354          * @event activate
6355          * Fires when this tab becomes the active tab.
6356          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6357          * @param {Roo.TabPanelItem} this
6358          */
6359         "activate": true,
6360         /**
6361          * @event beforeclose
6362          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
6363          * @param {Roo.TabPanelItem} this
6364          * @param {Object} e Set cancel to true on this object to cancel the close.
6365          */
6366         "beforeclose": true,
6367         /**
6368          * @event close
6369          * Fires when this tab is closed.
6370          * @param {Roo.TabPanelItem} this
6371          */
6372          "close": true,
6373         /**
6374          * @event deactivate
6375          * Fires when this tab is no longer the active tab.
6376          * @param {Roo.TabPanel} tabPanel The parent TabPanel
6377          * @param {Roo.TabPanelItem} this
6378          */
6379          "deactivate" : true
6380     });
6381     this.hidden = false;
6382
6383     Roo.TabPanelItem.superclass.constructor.call(this);
6384 };
6385
6386 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
6387     purgeListeners : function(){
6388        Roo.util.Observable.prototype.purgeListeners.call(this);
6389        this.el.removeAllListeners();
6390     },
6391     /**
6392      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
6393      */
6394     show : function(){
6395         this.pnode.addClass("on");
6396         this.showAction();
6397         if(Roo.isOpera){
6398             this.tabPanel.stripWrap.repaint();
6399         }
6400         this.fireEvent("activate", this.tabPanel, this);
6401     },
6402
6403     /**
6404      * Returns true if this tab is the active tab.
6405      * @return {Boolean}
6406      */
6407     isActive : function(){
6408         return this.tabPanel.getActiveTab() == this;
6409     },
6410
6411     /**
6412      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
6413      */
6414     hide : function(){
6415         this.pnode.removeClass("on");
6416         this.hideAction();
6417         this.fireEvent("deactivate", this.tabPanel, this);
6418     },
6419
6420     hideAction : function(){
6421         this.bodyEl.hide();
6422         this.bodyEl.setStyle("position", "absolute");
6423         this.bodyEl.setLeft("-20000px");
6424         this.bodyEl.setTop("-20000px");
6425     },
6426
6427     showAction : function(){
6428         this.bodyEl.setStyle("position", "relative");
6429         this.bodyEl.setTop("");
6430         this.bodyEl.setLeft("");
6431         this.bodyEl.show();
6432     },
6433
6434     /**
6435      * Set the tooltip for the tab.
6436      * @param {String} tooltip The tab's tooltip
6437      */
6438     setTooltip : function(text){
6439         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6440             this.textEl.dom.qtip = text;
6441             this.textEl.dom.removeAttribute('title');
6442         }else{
6443             this.textEl.dom.title = text;
6444         }
6445     },
6446
6447     onTabClick : function(e){
6448         e.preventDefault();
6449         this.tabPanel.activate(this.id);
6450     },
6451
6452     onTabMouseDown : function(e){
6453         e.preventDefault();
6454         this.tabPanel.activate(this.id);
6455     },
6456
6457     getWidth : function(){
6458         return this.inner.getWidth();
6459     },
6460
6461     setWidth : function(width){
6462         var iwidth = width - this.pnode.getPadding("lr");
6463         this.inner.setWidth(iwidth);
6464         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6465         this.pnode.setWidth(width);
6466     },
6467
6468     /**
6469      * Show or hide the tab
6470      * @param {Boolean} hidden True to hide or false to show.
6471      */
6472     setHidden : function(hidden){
6473         this.hidden = hidden;
6474         this.pnode.setStyle("display", hidden ? "none" : "");
6475     },
6476
6477     /**
6478      * Returns true if this tab is "hidden"
6479      * @return {Boolean}
6480      */
6481     isHidden : function(){
6482         return this.hidden;
6483     },
6484
6485     /**
6486      * Returns the text for this tab
6487      * @return {String}
6488      */
6489     getText : function(){
6490         return this.text;
6491     },
6492
6493     autoSize : function(){
6494         //this.el.beginMeasure();
6495         this.textEl.setWidth(1);
6496         /*
6497          *  #2804 [new] Tabs in Roojs
6498          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6499          */
6500         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6501         //this.el.endMeasure();
6502     },
6503
6504     /**
6505      * Sets the text for the tab (Note: this also sets the tooltip text)
6506      * @param {String} text The tab's text and tooltip
6507      */
6508     setText : function(text){
6509         this.text = text;
6510         this.textEl.update(text);
6511         this.setTooltip(text);
6512         if(!this.tabPanel.resizeTabs){
6513             this.autoSize();
6514         }
6515     },
6516     /**
6517      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6518      */
6519     activate : function(){
6520         this.tabPanel.activate(this.id);
6521     },
6522
6523     /**
6524      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6525      */
6526     disable : function(){
6527         if(this.tabPanel.active != this){
6528             this.disabled = true;
6529             this.pnode.addClass("disabled");
6530         }
6531     },
6532
6533     /**
6534      * Enables this TabPanelItem if it was previously disabled.
6535      */
6536     enable : function(){
6537         this.disabled = false;
6538         this.pnode.removeClass("disabled");
6539     },
6540
6541     /**
6542      * Sets the content for this TabPanelItem.
6543      * @param {String} content The content
6544      * @param {Boolean} loadScripts true to look for and load scripts
6545      */
6546     setContent : function(content, loadScripts){
6547         this.bodyEl.update(content, loadScripts);
6548     },
6549
6550     /**
6551      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6552      * @return {Roo.UpdateManager} The UpdateManager
6553      */
6554     getUpdateManager : function(){
6555         return this.bodyEl.getUpdateManager();
6556     },
6557
6558     /**
6559      * Set a URL to be used to load the content for this TabPanelItem.
6560      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6561      * @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)
6562      * @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)
6563      * @return {Roo.UpdateManager} The UpdateManager
6564      */
6565     setUrl : function(url, params, loadOnce){
6566         if(this.refreshDelegate){
6567             this.un('activate', this.refreshDelegate);
6568         }
6569         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6570         this.on("activate", this.refreshDelegate);
6571         return this.bodyEl.getUpdateManager();
6572     },
6573
6574     /** @private */
6575     _handleRefresh : function(url, params, loadOnce){
6576         if(!loadOnce || !this.loaded){
6577             var updater = this.bodyEl.getUpdateManager();
6578             updater.update(url, params, this._setLoaded.createDelegate(this));
6579         }
6580     },
6581
6582     /**
6583      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6584      *   Will fail silently if the setUrl method has not been called.
6585      *   This does not activate the panel, just updates its content.
6586      */
6587     refresh : function(){
6588         if(this.refreshDelegate){
6589            this.loaded = false;
6590            this.refreshDelegate();
6591         }
6592     },
6593
6594     /** @private */
6595     _setLoaded : function(){
6596         this.loaded = true;
6597     },
6598
6599     /** @private */
6600     closeClick : function(e){
6601         var o = {};
6602         e.stopEvent();
6603         this.fireEvent("beforeclose", this, o);
6604         if(o.cancel !== true){
6605             this.tabPanel.removeTab(this.id);
6606         }
6607     },
6608     /**
6609      * The text displayed in the tooltip for the close icon.
6610      * @type String
6611      */
6612     closeText : "Close this tab"
6613 });
6614
6615 /** @private */
6616 Roo.TabPanel.prototype.createStrip = function(container){
6617     var strip = document.createElement("div");
6618     strip.className = "x-tabs-wrap";
6619     container.appendChild(strip);
6620     return strip;
6621 };
6622 /** @private */
6623 Roo.TabPanel.prototype.createStripList = function(strip){
6624     // div wrapper for retard IE
6625     // returns the "tr" element.
6626     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6627         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6628         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6629     return strip.firstChild.firstChild.firstChild.firstChild;
6630 };
6631 /** @private */
6632 Roo.TabPanel.prototype.createBody = function(container){
6633     var body = document.createElement("div");
6634     Roo.id(body, "tab-body");
6635     Roo.fly(body).addClass("x-tabs-body");
6636     container.appendChild(body);
6637     return body;
6638 };
6639 /** @private */
6640 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6641     var body = Roo.getDom(id);
6642     if(!body){
6643         body = document.createElement("div");
6644         body.id = id;
6645     }
6646     Roo.fly(body).addClass("x-tabs-item-body");
6647     bodyEl.insertBefore(body, bodyEl.firstChild);
6648     return body;
6649 };
6650 /** @private */
6651 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6652     var td = document.createElement("td");
6653     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6654     //stripEl.appendChild(td);
6655     if(closable){
6656         td.className = "x-tabs-closable";
6657         if(!this.closeTpl){
6658             this.closeTpl = new Roo.Template(
6659                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6660                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6661                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6662             );
6663         }
6664         var el = this.closeTpl.overwrite(td, {"text": text});
6665         var close = el.getElementsByTagName("div")[0];
6666         var inner = el.getElementsByTagName("em")[0];
6667         return {"el": el, "close": close, "inner": inner};
6668     } else {
6669         if(!this.tabTpl){
6670             this.tabTpl = new Roo.Template(
6671                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6672                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6673             );
6674         }
6675         var el = this.tabTpl.overwrite(td, {"text": text});
6676         var inner = el.getElementsByTagName("em")[0];
6677         return {"el": el, "inner": inner};
6678     }
6679 };/*
6680  * Based on:
6681  * Ext JS Library 1.1.1
6682  * Copyright(c) 2006-2007, Ext JS, LLC.
6683  *
6684  * Originally Released Under LGPL - original licence link has changed is not relivant.
6685  *
6686  * Fork - LGPL
6687  * <script type="text/javascript">
6688  */
6689
6690 /**
6691  * @class Roo.Button
6692  * @extends Roo.util.Observable
6693  * Simple Button class
6694  * @cfg {String} text The button text
6695  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6696  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6697  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6698  * @cfg {Object} scope The scope of the handler
6699  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6700  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6701  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6702  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6703  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6704  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6705    applies if enableToggle = true)
6706  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6707  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6708   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6709  * @constructor
6710  * Create a new button
6711  * @param {Object} config The config object
6712  */
6713 Roo.Button = function(renderTo, config)
6714 {
6715     if (!config) {
6716         config = renderTo;
6717         renderTo = config.renderTo || false;
6718     }
6719     
6720     Roo.apply(this, config);
6721     this.addEvents({
6722         /**
6723              * @event click
6724              * Fires when this button is clicked
6725              * @param {Button} this
6726              * @param {EventObject} e The click event
6727              */
6728             "click" : true,
6729         /**
6730              * @event toggle
6731              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6732              * @param {Button} this
6733              * @param {Boolean} pressed
6734              */
6735             "toggle" : true,
6736         /**
6737              * @event mouseover
6738              * Fires when the mouse hovers over the button
6739              * @param {Button} this
6740              * @param {Event} e The event object
6741              */
6742         'mouseover' : true,
6743         /**
6744              * @event mouseout
6745              * Fires when the mouse exits the button
6746              * @param {Button} this
6747              * @param {Event} e The event object
6748              */
6749         'mouseout': true,
6750          /**
6751              * @event render
6752              * Fires when the button is rendered
6753              * @param {Button} this
6754              */
6755         'render': true
6756     });
6757     if(this.menu){
6758         this.menu = Roo.menu.MenuMgr.get(this.menu);
6759     }
6760     // register listeners first!!  - so render can be captured..
6761     Roo.util.Observable.call(this);
6762     if(renderTo){
6763         this.render(renderTo);
6764     }
6765     
6766   
6767 };
6768
6769 Roo.extend(Roo.Button, Roo.util.Observable, {
6770     /**
6771      * 
6772      */
6773     
6774     /**
6775      * Read-only. True if this button is hidden
6776      * @type Boolean
6777      */
6778     hidden : false,
6779     /**
6780      * Read-only. True if this button is disabled
6781      * @type Boolean
6782      */
6783     disabled : false,
6784     /**
6785      * Read-only. True if this button is pressed (only if enableToggle = true)
6786      * @type Boolean
6787      */
6788     pressed : false,
6789
6790     /**
6791      * @cfg {Number} tabIndex 
6792      * The DOM tabIndex for this button (defaults to undefined)
6793      */
6794     tabIndex : undefined,
6795
6796     /**
6797      * @cfg {Boolean} enableToggle
6798      * True to enable pressed/not pressed toggling (defaults to false)
6799      */
6800     enableToggle: false,
6801     /**
6802      * @cfg {Mixed} menu
6803      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6804      */
6805     menu : undefined,
6806     /**
6807      * @cfg {String} menuAlign
6808      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6809      */
6810     menuAlign : "tl-bl?",
6811
6812     /**
6813      * @cfg {String} iconCls
6814      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6815      */
6816     iconCls : undefined,
6817     /**
6818      * @cfg {String} type
6819      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6820      */
6821     type : 'button',
6822
6823     // private
6824     menuClassTarget: 'tr',
6825
6826     /**
6827      * @cfg {String} clickEvent
6828      * The type of event to map to the button's event handler (defaults to 'click')
6829      */
6830     clickEvent : 'click',
6831
6832     /**
6833      * @cfg {Boolean} handleMouseEvents
6834      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6835      */
6836     handleMouseEvents : true,
6837
6838     /**
6839      * @cfg {String} tooltipType
6840      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6841      */
6842     tooltipType : 'qtip',
6843
6844     /**
6845      * @cfg {String} cls
6846      * A CSS class to apply to the button's main element.
6847      */
6848     
6849     /**
6850      * @cfg {Roo.Template} template (Optional)
6851      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6852      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6853      * require code modifications if required elements (e.g. a button) aren't present.
6854      */
6855
6856     // private
6857     render : function(renderTo){
6858         var btn;
6859         if(this.hideParent){
6860             this.parentEl = Roo.get(renderTo);
6861         }
6862         if(!this.dhconfig){
6863             if(!this.template){
6864                 if(!Roo.Button.buttonTemplate){
6865                     // hideous table template
6866                     Roo.Button.buttonTemplate = new Roo.Template(
6867                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6868                         '<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>',
6869                         "</tr></tbody></table>");
6870                 }
6871                 this.template = Roo.Button.buttonTemplate;
6872             }
6873             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6874             var btnEl = btn.child("button:first");
6875             btnEl.on('focus', this.onFocus, this);
6876             btnEl.on('blur', this.onBlur, this);
6877             if(this.cls){
6878                 btn.addClass(this.cls);
6879             }
6880             if(this.icon){
6881                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6882             }
6883             if(this.iconCls){
6884                 btnEl.addClass(this.iconCls);
6885                 if(!this.cls){
6886                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6887                 }
6888             }
6889             if(this.tabIndex !== undefined){
6890                 btnEl.dom.tabIndex = this.tabIndex;
6891             }
6892             if(this.tooltip){
6893                 if(typeof this.tooltip == 'object'){
6894                     Roo.QuickTips.tips(Roo.apply({
6895                           target: btnEl.id
6896                     }, this.tooltip));
6897                 } else {
6898                     btnEl.dom[this.tooltipType] = this.tooltip;
6899                 }
6900             }
6901         }else{
6902             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6903         }
6904         this.el = btn;
6905         if(this.id){
6906             this.el.dom.id = this.el.id = this.id;
6907         }
6908         if(this.menu){
6909             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6910             this.menu.on("show", this.onMenuShow, this);
6911             this.menu.on("hide", this.onMenuHide, this);
6912         }
6913         btn.addClass("x-btn");
6914         if(Roo.isIE && !Roo.isIE7){
6915             this.autoWidth.defer(1, this);
6916         }else{
6917             this.autoWidth();
6918         }
6919         if(this.handleMouseEvents){
6920             btn.on("mouseover", this.onMouseOver, this);
6921             btn.on("mouseout", this.onMouseOut, this);
6922             btn.on("mousedown", this.onMouseDown, this);
6923         }
6924         btn.on(this.clickEvent, this.onClick, this);
6925         //btn.on("mouseup", this.onMouseUp, this);
6926         if(this.hidden){
6927             this.hide();
6928         }
6929         if(this.disabled){
6930             this.disable();
6931         }
6932         Roo.ButtonToggleMgr.register(this);
6933         if(this.pressed){
6934             this.el.addClass("x-btn-pressed");
6935         }
6936         if(this.repeat){
6937             var repeater = new Roo.util.ClickRepeater(btn,
6938                 typeof this.repeat == "object" ? this.repeat : {}
6939             );
6940             repeater.on("click", this.onClick,  this);
6941         }
6942         
6943         this.fireEvent('render', this);
6944         
6945     },
6946     /**
6947      * Returns the button's underlying element
6948      * @return {Roo.Element} The element
6949      */
6950     getEl : function(){
6951         return this.el;  
6952     },
6953     
6954     /**
6955      * Destroys this Button and removes any listeners.
6956      */
6957     destroy : function(){
6958         Roo.ButtonToggleMgr.unregister(this);
6959         this.el.removeAllListeners();
6960         this.purgeListeners();
6961         this.el.remove();
6962     },
6963
6964     // private
6965     autoWidth : function(){
6966         if(this.el){
6967             this.el.setWidth("auto");
6968             if(Roo.isIE7 && Roo.isStrict){
6969                 var ib = this.el.child('button');
6970                 if(ib && ib.getWidth() > 20){
6971                     ib.clip();
6972                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6973                 }
6974             }
6975             if(this.minWidth){
6976                 if(this.hidden){
6977                     this.el.beginMeasure();
6978                 }
6979                 if(this.el.getWidth() < this.minWidth){
6980                     this.el.setWidth(this.minWidth);
6981                 }
6982                 if(this.hidden){
6983                     this.el.endMeasure();
6984                 }
6985             }
6986         }
6987     },
6988
6989     /**
6990      * Assigns this button's click handler
6991      * @param {Function} handler The function to call when the button is clicked
6992      * @param {Object} scope (optional) Scope for the function passed in
6993      */
6994     setHandler : function(handler, scope){
6995         this.handler = handler;
6996         this.scope = scope;  
6997     },
6998     
6999     /**
7000      * Sets this button's text
7001      * @param {String} text The button text
7002      */
7003     setText : function(text){
7004         this.text = text;
7005         if(this.el){
7006             this.el.child("td.x-btn-center button.x-btn-text").update(text);
7007         }
7008         this.autoWidth();
7009     },
7010     
7011     /**
7012      * Gets the text for this button
7013      * @return {String} The button text
7014      */
7015     getText : function(){
7016         return this.text;  
7017     },
7018     
7019     /**
7020      * Show this button
7021      */
7022     show: function(){
7023         this.hidden = false;
7024         if(this.el){
7025             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
7026         }
7027     },
7028     
7029     /**
7030      * Hide this button
7031      */
7032     hide: function(){
7033         this.hidden = true;
7034         if(this.el){
7035             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
7036         }
7037     },
7038     
7039     /**
7040      * Convenience function for boolean show/hide
7041      * @param {Boolean} visible True to show, false to hide
7042      */
7043     setVisible: function(visible){
7044         if(visible) {
7045             this.show();
7046         }else{
7047             this.hide();
7048         }
7049     },
7050     
7051     /**
7052      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
7053      * @param {Boolean} state (optional) Force a particular state
7054      */
7055     toggle : function(state){
7056         state = state === undefined ? !this.pressed : state;
7057         if(state != this.pressed){
7058             if(state){
7059                 this.el.addClass("x-btn-pressed");
7060                 this.pressed = true;
7061                 this.fireEvent("toggle", this, true);
7062             }else{
7063                 this.el.removeClass("x-btn-pressed");
7064                 this.pressed = false;
7065                 this.fireEvent("toggle", this, false);
7066             }
7067             if(this.toggleHandler){
7068                 this.toggleHandler.call(this.scope || this, this, state);
7069             }
7070         }
7071     },
7072     
7073     /**
7074      * Focus the button
7075      */
7076     focus : function(){
7077         this.el.child('button:first').focus();
7078     },
7079     
7080     /**
7081      * Disable this button
7082      */
7083     disable : function(){
7084         if(this.el){
7085             this.el.addClass("x-btn-disabled");
7086         }
7087         this.disabled = true;
7088     },
7089     
7090     /**
7091      * Enable this button
7092      */
7093     enable : function(){
7094         if(this.el){
7095             this.el.removeClass("x-btn-disabled");
7096         }
7097         this.disabled = false;
7098     },
7099
7100     /**
7101      * Convenience function for boolean enable/disable
7102      * @param {Boolean} enabled True to enable, false to disable
7103      */
7104     setDisabled : function(v){
7105         this[v !== true ? "enable" : "disable"]();
7106     },
7107
7108     // private
7109     onClick : function(e)
7110     {
7111         if(e){
7112             e.preventDefault();
7113         }
7114         if(e.button != 0){
7115             return;
7116         }
7117         if(!this.disabled){
7118             if(this.enableToggle){
7119                 this.toggle();
7120             }
7121             if(this.menu && !this.menu.isVisible()){
7122                 this.menu.show(this.el, this.menuAlign);
7123             }
7124             this.fireEvent("click", this, e);
7125             if(this.handler){
7126                 this.el.removeClass("x-btn-over");
7127                 this.handler.call(this.scope || this, this, e);
7128             }
7129         }
7130     },
7131     // private
7132     onMouseOver : function(e){
7133         if(!this.disabled){
7134             this.el.addClass("x-btn-over");
7135             this.fireEvent('mouseover', this, e);
7136         }
7137     },
7138     // private
7139     onMouseOut : function(e){
7140         if(!e.within(this.el,  true)){
7141             this.el.removeClass("x-btn-over");
7142             this.fireEvent('mouseout', this, e);
7143         }
7144     },
7145     // private
7146     onFocus : function(e){
7147         if(!this.disabled){
7148             this.el.addClass("x-btn-focus");
7149         }
7150     },
7151     // private
7152     onBlur : function(e){
7153         this.el.removeClass("x-btn-focus");
7154     },
7155     // private
7156     onMouseDown : function(e){
7157         if(!this.disabled && e.button == 0){
7158             this.el.addClass("x-btn-click");
7159             Roo.get(document).on('mouseup', this.onMouseUp, this);
7160         }
7161     },
7162     // private
7163     onMouseUp : function(e){
7164         if(e.button == 0){
7165             this.el.removeClass("x-btn-click");
7166             Roo.get(document).un('mouseup', this.onMouseUp, this);
7167         }
7168     },
7169     // private
7170     onMenuShow : function(e){
7171         this.el.addClass("x-btn-menu-active");
7172     },
7173     // private
7174     onMenuHide : function(e){
7175         this.el.removeClass("x-btn-menu-active");
7176     }   
7177 });
7178
7179 // Private utility class used by Button
7180 Roo.ButtonToggleMgr = function(){
7181    var groups = {};
7182    
7183    function toggleGroup(btn, state){
7184        if(state){
7185            var g = groups[btn.toggleGroup];
7186            for(var i = 0, l = g.length; i < l; i++){
7187                if(g[i] != btn){
7188                    g[i].toggle(false);
7189                }
7190            }
7191        }
7192    }
7193    
7194    return {
7195        register : function(btn){
7196            if(!btn.toggleGroup){
7197                return;
7198            }
7199            var g = groups[btn.toggleGroup];
7200            if(!g){
7201                g = groups[btn.toggleGroup] = [];
7202            }
7203            g.push(btn);
7204            btn.on("toggle", toggleGroup);
7205        },
7206        
7207        unregister : function(btn){
7208            if(!btn.toggleGroup){
7209                return;
7210            }
7211            var g = groups[btn.toggleGroup];
7212            if(g){
7213                g.remove(btn);
7214                btn.un("toggle", toggleGroup);
7215            }
7216        }
7217    };
7218 }();/*
7219  * Based on:
7220  * Ext JS Library 1.1.1
7221  * Copyright(c) 2006-2007, Ext JS, LLC.
7222  *
7223  * Originally Released Under LGPL - original licence link has changed is not relivant.
7224  *
7225  * Fork - LGPL
7226  * <script type="text/javascript">
7227  */
7228  
7229 /**
7230  * @class Roo.SplitButton
7231  * @extends Roo.Button
7232  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
7233  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
7234  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
7235  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
7236  * @cfg {String} arrowTooltip The title attribute of the arrow
7237  * @constructor
7238  * Create a new menu button
7239  * @param {String/HTMLElement/Element} renderTo The element to append the button to
7240  * @param {Object} config The config object
7241  */
7242 Roo.SplitButton = function(renderTo, config){
7243     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
7244     /**
7245      * @event arrowclick
7246      * Fires when this button's arrow is clicked
7247      * @param {SplitButton} this
7248      * @param {EventObject} e The click event
7249      */
7250     this.addEvents({"arrowclick":true});
7251 };
7252
7253 Roo.extend(Roo.SplitButton, Roo.Button, {
7254     render : function(renderTo){
7255         // this is one sweet looking template!
7256         var tpl = new Roo.Template(
7257             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
7258             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
7259             '<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>',
7260             "</tbody></table></td><td>",
7261             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
7262             '<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>',
7263             "</tbody></table></td></tr></table>"
7264         );
7265         var btn = tpl.append(renderTo, [this.text, this.type], true);
7266         var btnEl = btn.child("button");
7267         if(this.cls){
7268             btn.addClass(this.cls);
7269         }
7270         if(this.icon){
7271             btnEl.setStyle('background-image', 'url(' +this.icon +')');
7272         }
7273         if(this.iconCls){
7274             btnEl.addClass(this.iconCls);
7275             if(!this.cls){
7276                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
7277             }
7278         }
7279         this.el = btn;
7280         if(this.handleMouseEvents){
7281             btn.on("mouseover", this.onMouseOver, this);
7282             btn.on("mouseout", this.onMouseOut, this);
7283             btn.on("mousedown", this.onMouseDown, this);
7284             btn.on("mouseup", this.onMouseUp, this);
7285         }
7286         btn.on(this.clickEvent, this.onClick, this);
7287         if(this.tooltip){
7288             if(typeof this.tooltip == 'object'){
7289                 Roo.QuickTips.tips(Roo.apply({
7290                       target: btnEl.id
7291                 }, this.tooltip));
7292             } else {
7293                 btnEl.dom[this.tooltipType] = this.tooltip;
7294             }
7295         }
7296         if(this.arrowTooltip){
7297             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
7298         }
7299         if(this.hidden){
7300             this.hide();
7301         }
7302         if(this.disabled){
7303             this.disable();
7304         }
7305         if(this.pressed){
7306             this.el.addClass("x-btn-pressed");
7307         }
7308         if(Roo.isIE && !Roo.isIE7){
7309             this.autoWidth.defer(1, this);
7310         }else{
7311             this.autoWidth();
7312         }
7313         if(this.menu){
7314             this.menu.on("show", this.onMenuShow, this);
7315             this.menu.on("hide", this.onMenuHide, this);
7316         }
7317         this.fireEvent('render', this);
7318     },
7319
7320     // private
7321     autoWidth : function(){
7322         if(this.el){
7323             var tbl = this.el.child("table:first");
7324             var tbl2 = this.el.child("table:last");
7325             this.el.setWidth("auto");
7326             tbl.setWidth("auto");
7327             if(Roo.isIE7 && Roo.isStrict){
7328                 var ib = this.el.child('button:first');
7329                 if(ib && ib.getWidth() > 20){
7330                     ib.clip();
7331                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
7332                 }
7333             }
7334             if(this.minWidth){
7335                 if(this.hidden){
7336                     this.el.beginMeasure();
7337                 }
7338                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
7339                     tbl.setWidth(this.minWidth-tbl2.getWidth());
7340                 }
7341                 if(this.hidden){
7342                     this.el.endMeasure();
7343                 }
7344             }
7345             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
7346         } 
7347     },
7348     /**
7349      * Sets this button's click handler
7350      * @param {Function} handler The function to call when the button is clicked
7351      * @param {Object} scope (optional) Scope for the function passed above
7352      */
7353     setHandler : function(handler, scope){
7354         this.handler = handler;
7355         this.scope = scope;  
7356     },
7357     
7358     /**
7359      * Sets this button's arrow click handler
7360      * @param {Function} handler The function to call when the arrow is clicked
7361      * @param {Object} scope (optional) Scope for the function passed above
7362      */
7363     setArrowHandler : function(handler, scope){
7364         this.arrowHandler = handler;
7365         this.scope = scope;  
7366     },
7367     
7368     /**
7369      * Focus the button
7370      */
7371     focus : function(){
7372         if(this.el){
7373             this.el.child("button:first").focus();
7374         }
7375     },
7376
7377     // private
7378     onClick : function(e){
7379         e.preventDefault();
7380         if(!this.disabled){
7381             if(e.getTarget(".x-btn-menu-arrow-wrap")){
7382                 if(this.menu && !this.menu.isVisible()){
7383                     this.menu.show(this.el, this.menuAlign);
7384                 }
7385                 this.fireEvent("arrowclick", this, e);
7386                 if(this.arrowHandler){
7387                     this.arrowHandler.call(this.scope || this, this, e);
7388                 }
7389             }else{
7390                 this.fireEvent("click", this, e);
7391                 if(this.handler){
7392                     this.handler.call(this.scope || this, this, e);
7393                 }
7394             }
7395         }
7396     },
7397     // private
7398     onMouseDown : function(e){
7399         if(!this.disabled){
7400             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
7401         }
7402     },
7403     // private
7404     onMouseUp : function(e){
7405         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
7406     }   
7407 });
7408
7409
7410 // backwards compat
7411 Roo.MenuButton = Roo.SplitButton;/*
7412  * Based on:
7413  * Ext JS Library 1.1.1
7414  * Copyright(c) 2006-2007, Ext JS, LLC.
7415  *
7416  * Originally Released Under LGPL - original licence link has changed is not relivant.
7417  *
7418  * Fork - LGPL
7419  * <script type="text/javascript">
7420  */
7421
7422 /**
7423  * @class Roo.Toolbar
7424  * Basic Toolbar class.
7425  * @constructor
7426  * Creates a new Toolbar
7427  * @param {Object} container The config object
7428  */ 
7429 Roo.Toolbar = function(container, buttons, config)
7430 {
7431     /// old consturctor format still supported..
7432     if(container instanceof Array){ // omit the container for later rendering
7433         buttons = container;
7434         config = buttons;
7435         container = null;
7436     }
7437     if (typeof(container) == 'object' && container.xtype) {
7438         config = container;
7439         container = config.container;
7440         buttons = config.buttons || []; // not really - use items!!
7441     }
7442     var xitems = [];
7443     if (config && config.items) {
7444         xitems = config.items;
7445         delete config.items;
7446     }
7447     Roo.apply(this, config);
7448     this.buttons = buttons;
7449     
7450     if(container){
7451         this.render(container);
7452     }
7453     this.xitems = xitems;
7454     Roo.each(xitems, function(b) {
7455         this.add(b);
7456     }, this);
7457     
7458 };
7459
7460 Roo.Toolbar.prototype = {
7461     /**
7462      * @cfg {Array} items
7463      * array of button configs or elements to add (will be converted to a MixedCollection)
7464      */
7465     
7466     /**
7467      * @cfg {String/HTMLElement/Element} container
7468      * The id or element that will contain the toolbar
7469      */
7470     // private
7471     render : function(ct){
7472         this.el = Roo.get(ct);
7473         if(this.cls){
7474             this.el.addClass(this.cls);
7475         }
7476         // using a table allows for vertical alignment
7477         // 100% width is needed by Safari...
7478         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7479         this.tr = this.el.child("tr", true);
7480         var autoId = 0;
7481         this.items = new Roo.util.MixedCollection(false, function(o){
7482             return o.id || ("item" + (++autoId));
7483         });
7484         if(this.buttons){
7485             this.add.apply(this, this.buttons);
7486             delete this.buttons;
7487         }
7488     },
7489
7490     /**
7491      * Adds element(s) to the toolbar -- this function takes a variable number of 
7492      * arguments of mixed type and adds them to the toolbar.
7493      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7494      * <ul>
7495      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7496      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7497      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7498      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7499      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7500      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7501      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7502      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7503      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7504      * </ul>
7505      * @param {Mixed} arg2
7506      * @param {Mixed} etc.
7507      */
7508     add : function(){
7509         var a = arguments, l = a.length;
7510         for(var i = 0; i < l; i++){
7511             this._add(a[i]);
7512         }
7513     },
7514     // private..
7515     _add : function(el) {
7516         
7517         if (el.xtype) {
7518             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7519         }
7520         
7521         if (el.applyTo){ // some kind of form field
7522             return this.addField(el);
7523         } 
7524         if (el.render){ // some kind of Toolbar.Item
7525             return this.addItem(el);
7526         }
7527         if (typeof el == "string"){ // string
7528             if(el == "separator" || el == "-"){
7529                 return this.addSeparator();
7530             }
7531             if (el == " "){
7532                 return this.addSpacer();
7533             }
7534             if(el == "->"){
7535                 return this.addFill();
7536             }
7537             return this.addText(el);
7538             
7539         }
7540         if(el.tagName){ // element
7541             return this.addElement(el);
7542         }
7543         if(typeof el == "object"){ // must be button config?
7544             return this.addButton(el);
7545         }
7546         // and now what?!?!
7547         return false;
7548         
7549     },
7550     
7551     /**
7552      * Add an Xtype element
7553      * @param {Object} xtype Xtype Object
7554      * @return {Object} created Object
7555      */
7556     addxtype : function(e){
7557         return this.add(e);  
7558     },
7559     
7560     /**
7561      * Returns the Element for this toolbar.
7562      * @return {Roo.Element}
7563      */
7564     getEl : function(){
7565         return this.el;  
7566     },
7567     
7568     /**
7569      * Adds a separator
7570      * @return {Roo.Toolbar.Item} The separator item
7571      */
7572     addSeparator : function(){
7573         return this.addItem(new Roo.Toolbar.Separator());
7574     },
7575
7576     /**
7577      * Adds a spacer element
7578      * @return {Roo.Toolbar.Spacer} The spacer item
7579      */
7580     addSpacer : function(){
7581         return this.addItem(new Roo.Toolbar.Spacer());
7582     },
7583
7584     /**
7585      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7586      * @return {Roo.Toolbar.Fill} The fill item
7587      */
7588     addFill : function(){
7589         return this.addItem(new Roo.Toolbar.Fill());
7590     },
7591
7592     /**
7593      * Adds any standard HTML element to the toolbar
7594      * @param {String/HTMLElement/Element} el The element or id of the element to add
7595      * @return {Roo.Toolbar.Item} The element's item
7596      */
7597     addElement : function(el){
7598         return this.addItem(new Roo.Toolbar.Item(el));
7599     },
7600     /**
7601      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7602      * @type Roo.util.MixedCollection  
7603      */
7604     items : false,
7605      
7606     /**
7607      * Adds any Toolbar.Item or subclass
7608      * @param {Roo.Toolbar.Item} item
7609      * @return {Roo.Toolbar.Item} The item
7610      */
7611     addItem : function(item){
7612         var td = this.nextBlock();
7613         item.render(td);
7614         this.items.add(item);
7615         return item;
7616     },
7617     
7618     /**
7619      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7620      * @param {Object/Array} config A button config or array of configs
7621      * @return {Roo.Toolbar.Button/Array}
7622      */
7623     addButton : function(config){
7624         if(config instanceof Array){
7625             var buttons = [];
7626             for(var i = 0, len = config.length; i < len; i++) {
7627                 buttons.push(this.addButton(config[i]));
7628             }
7629             return buttons;
7630         }
7631         var b = config;
7632         if(!(config instanceof Roo.Toolbar.Button)){
7633             b = config.split ?
7634                 new Roo.Toolbar.SplitButton(config) :
7635                 new Roo.Toolbar.Button(config);
7636         }
7637         var td = this.nextBlock();
7638         b.render(td);
7639         this.items.add(b);
7640         return b;
7641     },
7642     
7643     /**
7644      * Adds text to the toolbar
7645      * @param {String} text The text to add
7646      * @return {Roo.Toolbar.Item} The element's item
7647      */
7648     addText : function(text){
7649         return this.addItem(new Roo.Toolbar.TextItem(text));
7650     },
7651     
7652     /**
7653      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7654      * @param {Number} index The index where the item is to be inserted
7655      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7656      * @return {Roo.Toolbar.Button/Item}
7657      */
7658     insertButton : function(index, item){
7659         if(item instanceof Array){
7660             var buttons = [];
7661             for(var i = 0, len = item.length; i < len; i++) {
7662                buttons.push(this.insertButton(index + i, item[i]));
7663             }
7664             return buttons;
7665         }
7666         if (!(item instanceof Roo.Toolbar.Button)){
7667            item = new Roo.Toolbar.Button(item);
7668         }
7669         var td = document.createElement("td");
7670         this.tr.insertBefore(td, this.tr.childNodes[index]);
7671         item.render(td);
7672         this.items.insert(index, item);
7673         return item;
7674     },
7675     
7676     /**
7677      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7678      * @param {Object} config
7679      * @return {Roo.Toolbar.Item} The element's item
7680      */
7681     addDom : function(config, returnEl){
7682         var td = this.nextBlock();
7683         Roo.DomHelper.overwrite(td, config);
7684         var ti = new Roo.Toolbar.Item(td.firstChild);
7685         ti.render(td);
7686         this.items.add(ti);
7687         return ti;
7688     },
7689
7690     /**
7691      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7692      * @type Roo.util.MixedCollection  
7693      */
7694     fields : false,
7695     
7696     /**
7697      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7698      * Note: the field should not have been rendered yet. For a field that has already been
7699      * rendered, use {@link #addElement}.
7700      * @param {Roo.form.Field} field
7701      * @return {Roo.ToolbarItem}
7702      */
7703      
7704       
7705     addField : function(field) {
7706         if (!this.fields) {
7707             var autoId = 0;
7708             this.fields = new Roo.util.MixedCollection(false, function(o){
7709                 return o.id || ("item" + (++autoId));
7710             });
7711
7712         }
7713         
7714         var td = this.nextBlock();
7715         field.render(td);
7716         var ti = new Roo.Toolbar.Item(td.firstChild);
7717         ti.render(td);
7718         this.items.add(ti);
7719         this.fields.add(field);
7720         return ti;
7721     },
7722     /**
7723      * Hide the toolbar
7724      * @method hide
7725      */
7726      
7727       
7728     hide : function()
7729     {
7730         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7731         this.el.child('div').hide();
7732     },
7733     /**
7734      * Show the toolbar
7735      * @method show
7736      */
7737     show : function()
7738     {
7739         this.el.child('div').show();
7740     },
7741       
7742     // private
7743     nextBlock : function(){
7744         var td = document.createElement("td");
7745         this.tr.appendChild(td);
7746         return td;
7747     },
7748
7749     // private
7750     destroy : function(){
7751         if(this.items){ // rendered?
7752             Roo.destroy.apply(Roo, this.items.items);
7753         }
7754         if(this.fields){ // rendered?
7755             Roo.destroy.apply(Roo, this.fields.items);
7756         }
7757         Roo.Element.uncache(this.el, this.tr);
7758     }
7759 };
7760
7761 /**
7762  * @class Roo.Toolbar.Item
7763  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7764  * @constructor
7765  * Creates a new Item
7766  * @param {HTMLElement} el 
7767  */
7768 Roo.Toolbar.Item = function(el){
7769     var cfg = {};
7770     if (typeof (el.xtype) != 'undefined') {
7771         cfg = el;
7772         el = cfg.el;
7773     }
7774     
7775     this.el = Roo.getDom(el);
7776     this.id = Roo.id(this.el);
7777     this.hidden = false;
7778     
7779     this.addEvents({
7780          /**
7781              * @event render
7782              * Fires when the button is rendered
7783              * @param {Button} this
7784              */
7785         'render': true
7786     });
7787     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7788 };
7789 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7790 //Roo.Toolbar.Item.prototype = {
7791     
7792     /**
7793      * Get this item's HTML Element
7794      * @return {HTMLElement}
7795      */
7796     getEl : function(){
7797        return this.el;  
7798     },
7799
7800     // private
7801     render : function(td){
7802         
7803          this.td = td;
7804         td.appendChild(this.el);
7805         
7806         this.fireEvent('render', this);
7807     },
7808     
7809     /**
7810      * Removes and destroys this item.
7811      */
7812     destroy : function(){
7813         this.td.parentNode.removeChild(this.td);
7814     },
7815     
7816     /**
7817      * Shows this item.
7818      */
7819     show: function(){
7820         this.hidden = false;
7821         this.td.style.display = "";
7822     },
7823     
7824     /**
7825      * Hides this item.
7826      */
7827     hide: function(){
7828         this.hidden = true;
7829         this.td.style.display = "none";
7830     },
7831     
7832     /**
7833      * Convenience function for boolean show/hide.
7834      * @param {Boolean} visible true to show/false to hide
7835      */
7836     setVisible: function(visible){
7837         if(visible) {
7838             this.show();
7839         }else{
7840             this.hide();
7841         }
7842     },
7843     
7844     /**
7845      * Try to focus this item.
7846      */
7847     focus : function(){
7848         Roo.fly(this.el).focus();
7849     },
7850     
7851     /**
7852      * Disables this item.
7853      */
7854     disable : function(){
7855         Roo.fly(this.td).addClass("x-item-disabled");
7856         this.disabled = true;
7857         this.el.disabled = true;
7858     },
7859     
7860     /**
7861      * Enables this item.
7862      */
7863     enable : function(){
7864         Roo.fly(this.td).removeClass("x-item-disabled");
7865         this.disabled = false;
7866         this.el.disabled = false;
7867     }
7868 });
7869
7870
7871 /**
7872  * @class Roo.Toolbar.Separator
7873  * @extends Roo.Toolbar.Item
7874  * A simple toolbar separator class
7875  * @constructor
7876  * Creates a new Separator
7877  */
7878 Roo.Toolbar.Separator = function(cfg){
7879     
7880     var s = document.createElement("span");
7881     s.className = "ytb-sep";
7882     if (cfg) {
7883         cfg.el = s;
7884     }
7885     
7886     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7887 };
7888 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7889     enable:Roo.emptyFn,
7890     disable:Roo.emptyFn,
7891     focus:Roo.emptyFn
7892 });
7893
7894 /**
7895  * @class Roo.Toolbar.Spacer
7896  * @extends Roo.Toolbar.Item
7897  * A simple element that adds extra horizontal space to a toolbar.
7898  * @constructor
7899  * Creates a new Spacer
7900  */
7901 Roo.Toolbar.Spacer = function(cfg){
7902     var s = document.createElement("div");
7903     s.className = "ytb-spacer";
7904     if (cfg) {
7905         cfg.el = s;
7906     }
7907     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7908 };
7909 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7910     enable:Roo.emptyFn,
7911     disable:Roo.emptyFn,
7912     focus:Roo.emptyFn
7913 });
7914
7915 /**
7916  * @class Roo.Toolbar.Fill
7917  * @extends Roo.Toolbar.Spacer
7918  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7919  * @constructor
7920  * Creates a new Spacer
7921  */
7922 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7923     // private
7924     render : function(td){
7925         td.style.width = '100%';
7926         Roo.Toolbar.Fill.superclass.render.call(this, td);
7927     }
7928 });
7929
7930 /**
7931  * @class Roo.Toolbar.TextItem
7932  * @extends Roo.Toolbar.Item
7933  * A simple class that renders text directly into a toolbar.
7934  * @constructor
7935  * Creates a new TextItem
7936  * @param {String} text
7937  */
7938 Roo.Toolbar.TextItem = function(cfg){
7939     var  text = cfg || "";
7940     if (typeof(cfg) == 'object') {
7941         text = cfg.text || "";
7942     }  else {
7943         cfg = null;
7944     }
7945     var s = document.createElement("span");
7946     s.className = "ytb-text";
7947     s.innerHTML = text;
7948     if (cfg) {
7949         cfg.el  = s;
7950     }
7951     
7952     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7953 };
7954 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7955     
7956      
7957     enable:Roo.emptyFn,
7958     disable:Roo.emptyFn,
7959     focus:Roo.emptyFn
7960 });
7961
7962 /**
7963  * @class Roo.Toolbar.Button
7964  * @extends Roo.Button
7965  * A button that renders into a toolbar.
7966  * @constructor
7967  * Creates a new Button
7968  * @param {Object} config A standard {@link Roo.Button} config object
7969  */
7970 Roo.Toolbar.Button = function(config){
7971     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7972 };
7973 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7974     render : function(td){
7975         this.td = td;
7976         Roo.Toolbar.Button.superclass.render.call(this, td);
7977     },
7978     
7979     /**
7980      * Removes and destroys this button
7981      */
7982     destroy : function(){
7983         Roo.Toolbar.Button.superclass.destroy.call(this);
7984         this.td.parentNode.removeChild(this.td);
7985     },
7986     
7987     /**
7988      * Shows this button
7989      */
7990     show: function(){
7991         this.hidden = false;
7992         this.td.style.display = "";
7993     },
7994     
7995     /**
7996      * Hides this button
7997      */
7998     hide: function(){
7999         this.hidden = true;
8000         this.td.style.display = "none";
8001     },
8002
8003     /**
8004      * Disables this item
8005      */
8006     disable : function(){
8007         Roo.fly(this.td).addClass("x-item-disabled");
8008         this.disabled = true;
8009     },
8010
8011     /**
8012      * Enables this item
8013      */
8014     enable : function(){
8015         Roo.fly(this.td).removeClass("x-item-disabled");
8016         this.disabled = false;
8017     }
8018 });
8019 // backwards compat
8020 Roo.ToolbarButton = Roo.Toolbar.Button;
8021
8022 /**
8023  * @class Roo.Toolbar.SplitButton
8024  * @extends Roo.SplitButton
8025  * A menu button that renders into a toolbar.
8026  * @constructor
8027  * Creates a new SplitButton
8028  * @param {Object} config A standard {@link Roo.SplitButton} config object
8029  */
8030 Roo.Toolbar.SplitButton = function(config){
8031     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
8032 };
8033 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
8034     render : function(td){
8035         this.td = td;
8036         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
8037     },
8038     
8039     /**
8040      * Removes and destroys this button
8041      */
8042     destroy : function(){
8043         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
8044         this.td.parentNode.removeChild(this.td);
8045     },
8046     
8047     /**
8048      * Shows this button
8049      */
8050     show: function(){
8051         this.hidden = false;
8052         this.td.style.display = "";
8053     },
8054     
8055     /**
8056      * Hides this button
8057      */
8058     hide: function(){
8059         this.hidden = true;
8060         this.td.style.display = "none";
8061     }
8062 });
8063
8064 // backwards compat
8065 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
8066  * Based on:
8067  * Ext JS Library 1.1.1
8068  * Copyright(c) 2006-2007, Ext JS, LLC.
8069  *
8070  * Originally Released Under LGPL - original licence link has changed is not relivant.
8071  *
8072  * Fork - LGPL
8073  * <script type="text/javascript">
8074  */
8075  
8076 /**
8077  * @class Roo.PagingToolbar
8078  * @extends Roo.Toolbar
8079  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
8080  * @constructor
8081  * Create a new PagingToolbar
8082  * @param {Object} config The config object
8083  */
8084 Roo.PagingToolbar = function(el, ds, config)
8085 {
8086     // old args format still supported... - xtype is prefered..
8087     if (typeof(el) == 'object' && el.xtype) {
8088         // created from xtype...
8089         config = el;
8090         ds = el.dataSource;
8091         el = config.container;
8092     }
8093     var items = [];
8094     if (config.items) {
8095         items = config.items;
8096         config.items = [];
8097     }
8098     
8099     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
8100     this.ds = ds;
8101     this.cursor = 0;
8102     this.renderButtons(this.el);
8103     this.bind(ds);
8104     
8105     // supprot items array.
8106    
8107     Roo.each(items, function(e) {
8108         this.add(Roo.factory(e));
8109     },this);
8110     
8111 };
8112
8113 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
8114     /**
8115      * @cfg {Roo.data.Store} dataSource
8116      * The underlying data store providing the paged data
8117      */
8118     /**
8119      * @cfg {String/HTMLElement/Element} container
8120      * container The id or element that will contain the toolbar
8121      */
8122     /**
8123      * @cfg {Boolean} displayInfo
8124      * True to display the displayMsg (defaults to false)
8125      */
8126     /**
8127      * @cfg {Number} pageSize
8128      * The number of records to display per page (defaults to 20)
8129      */
8130     pageSize: 20,
8131     /**
8132      * @cfg {String} displayMsg
8133      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
8134      */
8135     displayMsg : 'Displaying {0} - {1} of {2}',
8136     /**
8137      * @cfg {String} emptyMsg
8138      * The message to display when no records are found (defaults to "No data to display")
8139      */
8140     emptyMsg : 'No data to display',
8141     /**
8142      * Customizable piece of the default paging text (defaults to "Page")
8143      * @type String
8144      */
8145     beforePageText : "Page",
8146     /**
8147      * Customizable piece of the default paging text (defaults to "of %0")
8148      * @type String
8149      */
8150     afterPageText : "of {0}",
8151     /**
8152      * Customizable piece of the default paging text (defaults to "First Page")
8153      * @type String
8154      */
8155     firstText : "First Page",
8156     /**
8157      * Customizable piece of the default paging text (defaults to "Previous Page")
8158      * @type String
8159      */
8160     prevText : "Previous Page",
8161     /**
8162      * Customizable piece of the default paging text (defaults to "Next Page")
8163      * @type String
8164      */
8165     nextText : "Next Page",
8166     /**
8167      * Customizable piece of the default paging text (defaults to "Last Page")
8168      * @type String
8169      */
8170     lastText : "Last Page",
8171     /**
8172      * Customizable piece of the default paging text (defaults to "Refresh")
8173      * @type String
8174      */
8175     refreshText : "Refresh",
8176
8177     // private
8178     renderButtons : function(el){
8179         Roo.PagingToolbar.superclass.render.call(this, el);
8180         this.first = this.addButton({
8181             tooltip: this.firstText,
8182             cls: "x-btn-icon x-grid-page-first",
8183             disabled: true,
8184             handler: this.onClick.createDelegate(this, ["first"])
8185         });
8186         this.prev = this.addButton({
8187             tooltip: this.prevText,
8188             cls: "x-btn-icon x-grid-page-prev",
8189             disabled: true,
8190             handler: this.onClick.createDelegate(this, ["prev"])
8191         });
8192         //this.addSeparator();
8193         this.add(this.beforePageText);
8194         this.field = Roo.get(this.addDom({
8195            tag: "input",
8196            type: "text",
8197            size: "3",
8198            value: "1",
8199            cls: "x-grid-page-number"
8200         }).el);
8201         this.field.on("keydown", this.onPagingKeydown, this);
8202         this.field.on("focus", function(){this.dom.select();});
8203         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
8204         this.field.setHeight(18);
8205         //this.addSeparator();
8206         this.next = this.addButton({
8207             tooltip: this.nextText,
8208             cls: "x-btn-icon x-grid-page-next",
8209             disabled: true,
8210             handler: this.onClick.createDelegate(this, ["next"])
8211         });
8212         this.last = this.addButton({
8213             tooltip: this.lastText,
8214             cls: "x-btn-icon x-grid-page-last",
8215             disabled: true,
8216             handler: this.onClick.createDelegate(this, ["last"])
8217         });
8218         //this.addSeparator();
8219         this.loading = this.addButton({
8220             tooltip: this.refreshText,
8221             cls: "x-btn-icon x-grid-loading",
8222             handler: this.onClick.createDelegate(this, ["refresh"])
8223         });
8224
8225         if(this.displayInfo){
8226             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
8227         }
8228     },
8229
8230     // private
8231     updateInfo : function(){
8232         if(this.displayEl){
8233             var count = this.ds.getCount();
8234             var msg = count == 0 ?
8235                 this.emptyMsg :
8236                 String.format(
8237                     this.displayMsg,
8238                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
8239                 );
8240             this.displayEl.update(msg);
8241         }
8242     },
8243
8244     // private
8245     onLoad : function(ds, r, o){
8246        this.cursor = o.params ? o.params.start : 0;
8247        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
8248
8249        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
8250        this.field.dom.value = ap;
8251        this.first.setDisabled(ap == 1);
8252        this.prev.setDisabled(ap == 1);
8253        this.next.setDisabled(ap == ps);
8254        this.last.setDisabled(ap == ps);
8255        this.loading.enable();
8256        this.updateInfo();
8257     },
8258
8259     // private
8260     getPageData : function(){
8261         var total = this.ds.getTotalCount();
8262         return {
8263             total : total,
8264             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
8265             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
8266         };
8267     },
8268
8269     // private
8270     onLoadError : function(){
8271         this.loading.enable();
8272     },
8273
8274     // private
8275     onPagingKeydown : function(e){
8276         var k = e.getKey();
8277         var d = this.getPageData();
8278         if(k == e.RETURN){
8279             var v = this.field.dom.value, pageNum;
8280             if(!v || isNaN(pageNum = parseInt(v, 10))){
8281                 this.field.dom.value = d.activePage;
8282                 return;
8283             }
8284             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
8285             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8286             e.stopEvent();
8287         }
8288         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))
8289         {
8290           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
8291           this.field.dom.value = pageNum;
8292           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
8293           e.stopEvent();
8294         }
8295         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
8296         {
8297           var v = this.field.dom.value, pageNum; 
8298           var increment = (e.shiftKey) ? 10 : 1;
8299           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
8300             increment *= -1;
8301           }
8302           if(!v || isNaN(pageNum = parseInt(v, 10))) {
8303             this.field.dom.value = d.activePage;
8304             return;
8305           }
8306           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
8307           {
8308             this.field.dom.value = parseInt(v, 10) + increment;
8309             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
8310             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
8311           }
8312           e.stopEvent();
8313         }
8314     },
8315
8316     // private
8317     beforeLoad : function(){
8318         if(this.loading){
8319             this.loading.disable();
8320         }
8321     },
8322
8323     // private
8324     onClick : function(which){
8325         var ds = this.ds;
8326         switch(which){
8327             case "first":
8328                 ds.load({params:{start: 0, limit: this.pageSize}});
8329             break;
8330             case "prev":
8331                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
8332             break;
8333             case "next":
8334                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
8335             break;
8336             case "last":
8337                 var total = ds.getTotalCount();
8338                 var extra = total % this.pageSize;
8339                 var lastStart = extra ? (total - extra) : total-this.pageSize;
8340                 ds.load({params:{start: lastStart, limit: this.pageSize}});
8341             break;
8342             case "refresh":
8343                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
8344             break;
8345         }
8346     },
8347
8348     /**
8349      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
8350      * @param {Roo.data.Store} store The data store to unbind
8351      */
8352     unbind : function(ds){
8353         ds.un("beforeload", this.beforeLoad, this);
8354         ds.un("load", this.onLoad, this);
8355         ds.un("loadexception", this.onLoadError, this);
8356         ds.un("remove", this.updateInfo, this);
8357         ds.un("add", this.updateInfo, this);
8358         this.ds = undefined;
8359     },
8360
8361     /**
8362      * Binds the paging toolbar to the specified {@link Roo.data.Store}
8363      * @param {Roo.data.Store} store The data store to bind
8364      */
8365     bind : function(ds){
8366         ds.on("beforeload", this.beforeLoad, this);
8367         ds.on("load", this.onLoad, this);
8368         ds.on("loadexception", this.onLoadError, this);
8369         ds.on("remove", this.updateInfo, this);
8370         ds.on("add", this.updateInfo, this);
8371         this.ds = ds;
8372     }
8373 });/*
8374  * Based on:
8375  * Ext JS Library 1.1.1
8376  * Copyright(c) 2006-2007, Ext JS, LLC.
8377  *
8378  * Originally Released Under LGPL - original licence link has changed is not relivant.
8379  *
8380  * Fork - LGPL
8381  * <script type="text/javascript">
8382  */
8383
8384 /**
8385  * @class Roo.Resizable
8386  * @extends Roo.util.Observable
8387  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
8388  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
8389  * 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
8390  * the element will be wrapped for you automatically.</p>
8391  * <p>Here is the list of valid resize handles:</p>
8392  * <pre>
8393 Value   Description
8394 ------  -------------------
8395  'n'     north
8396  's'     south
8397  'e'     east
8398  'w'     west
8399  'nw'    northwest
8400  'sw'    southwest
8401  'se'    southeast
8402  'ne'    northeast
8403  'hd'    horizontal drag
8404  'all'   all
8405 </pre>
8406  * <p>Here's an example showing the creation of a typical Resizable:</p>
8407  * <pre><code>
8408 var resizer = new Roo.Resizable("element-id", {
8409     handles: 'all',
8410     minWidth: 200,
8411     minHeight: 100,
8412     maxWidth: 500,
8413     maxHeight: 400,
8414     pinned: true
8415 });
8416 resizer.on("resize", myHandler);
8417 </code></pre>
8418  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
8419  * resizer.east.setDisplayed(false);</p>
8420  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
8421  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
8422  * resize operation's new size (defaults to [0, 0])
8423  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
8424  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
8425  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
8426  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
8427  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
8428  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
8429  * @cfg {Number} width The width of the element in pixels (defaults to null)
8430  * @cfg {Number} height The height of the element in pixels (defaults to null)
8431  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8432  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8433  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8434  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8435  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8436  * in favor of the handles config option (defaults to false)
8437  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8438  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8439  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8440  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8441  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8442  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8443  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8444  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8445  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8446  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8447  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8448  * @constructor
8449  * Create a new resizable component
8450  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8451  * @param {Object} config configuration options
8452   */
8453 Roo.Resizable = function(el, config)
8454 {
8455     this.el = Roo.get(el);
8456
8457     if(config && config.wrap){
8458         config.resizeChild = this.el;
8459         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8460         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8461         this.el.setStyle("overflow", "hidden");
8462         this.el.setPositioning(config.resizeChild.getPositioning());
8463         config.resizeChild.clearPositioning();
8464         if(!config.width || !config.height){
8465             var csize = config.resizeChild.getSize();
8466             this.el.setSize(csize.width, csize.height);
8467         }
8468         if(config.pinned && !config.adjustments){
8469             config.adjustments = "auto";
8470         }
8471     }
8472
8473     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8474     this.proxy.unselectable();
8475     this.proxy.enableDisplayMode('block');
8476
8477     Roo.apply(this, config);
8478
8479     if(this.pinned){
8480         this.disableTrackOver = true;
8481         this.el.addClass("x-resizable-pinned");
8482     }
8483     // if the element isn't positioned, make it relative
8484     var position = this.el.getStyle("position");
8485     if(position != "absolute" && position != "fixed"){
8486         this.el.setStyle("position", "relative");
8487     }
8488     if(!this.handles){ // no handles passed, must be legacy style
8489         this.handles = 's,e,se';
8490         if(this.multiDirectional){
8491             this.handles += ',n,w';
8492         }
8493     }
8494     if(this.handles == "all"){
8495         this.handles = "n s e w ne nw se sw";
8496     }
8497     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8498     var ps = Roo.Resizable.positions;
8499     for(var i = 0, len = hs.length; i < len; i++){
8500         if(hs[i] && ps[hs[i]]){
8501             var pos = ps[hs[i]];
8502             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8503         }
8504     }
8505     // legacy
8506     this.corner = this.southeast;
8507     
8508     // updateBox = the box can move..
8509     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8510         this.updateBox = true;
8511     }
8512
8513     this.activeHandle = null;
8514
8515     if(this.resizeChild){
8516         if(typeof this.resizeChild == "boolean"){
8517             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8518         }else{
8519             this.resizeChild = Roo.get(this.resizeChild, true);
8520         }
8521     }
8522     
8523     if(this.adjustments == "auto"){
8524         var rc = this.resizeChild;
8525         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8526         if(rc && (hw || hn)){
8527             rc.position("relative");
8528             rc.setLeft(hw ? hw.el.getWidth() : 0);
8529             rc.setTop(hn ? hn.el.getHeight() : 0);
8530         }
8531         this.adjustments = [
8532             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8533             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8534         ];
8535     }
8536
8537     if(this.draggable){
8538         this.dd = this.dynamic ?
8539             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8540         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8541     }
8542
8543     // public events
8544     this.addEvents({
8545         /**
8546          * @event beforeresize
8547          * Fired before resize is allowed. Set enabled to false to cancel resize.
8548          * @param {Roo.Resizable} this
8549          * @param {Roo.EventObject} e The mousedown event
8550          */
8551         "beforeresize" : true,
8552         /**
8553          * @event resizing
8554          * Fired a resizing.
8555          * @param {Roo.Resizable} this
8556          * @param {Number} x The new x position
8557          * @param {Number} y The new y position
8558          * @param {Number} w The new w width
8559          * @param {Number} h The new h hight
8560          * @param {Roo.EventObject} e The mouseup event
8561          */
8562         "resizing" : true,
8563         /**
8564          * @event resize
8565          * Fired after a resize.
8566          * @param {Roo.Resizable} this
8567          * @param {Number} width The new width
8568          * @param {Number} height The new height
8569          * @param {Roo.EventObject} e The mouseup event
8570          */
8571         "resize" : true
8572     });
8573
8574     if(this.width !== null && this.height !== null){
8575         this.resizeTo(this.width, this.height);
8576     }else{
8577         this.updateChildSize();
8578     }
8579     if(Roo.isIE){
8580         this.el.dom.style.zoom = 1;
8581     }
8582     Roo.Resizable.superclass.constructor.call(this);
8583 };
8584
8585 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8586         resizeChild : false,
8587         adjustments : [0, 0],
8588         minWidth : 5,
8589         minHeight : 5,
8590         maxWidth : 10000,
8591         maxHeight : 10000,
8592         enabled : true,
8593         animate : false,
8594         duration : .35,
8595         dynamic : false,
8596         handles : false,
8597         multiDirectional : false,
8598         disableTrackOver : false,
8599         easing : 'easeOutStrong',
8600         widthIncrement : 0,
8601         heightIncrement : 0,
8602         pinned : false,
8603         width : null,
8604         height : null,
8605         preserveRatio : false,
8606         transparent: false,
8607         minX: 0,
8608         minY: 0,
8609         draggable: false,
8610
8611         /**
8612          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8613          */
8614         constrainTo: undefined,
8615         /**
8616          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8617          */
8618         resizeRegion: undefined,
8619
8620
8621     /**
8622      * Perform a manual resize
8623      * @param {Number} width
8624      * @param {Number} height
8625      */
8626     resizeTo : function(width, height){
8627         this.el.setSize(width, height);
8628         this.updateChildSize();
8629         this.fireEvent("resize", this, width, height, null);
8630     },
8631
8632     // private
8633     startSizing : function(e, handle){
8634         this.fireEvent("beforeresize", this, e);
8635         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8636
8637             if(!this.overlay){
8638                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8639                 this.overlay.unselectable();
8640                 this.overlay.enableDisplayMode("block");
8641                 this.overlay.on("mousemove", this.onMouseMove, this);
8642                 this.overlay.on("mouseup", this.onMouseUp, this);
8643             }
8644             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8645
8646             this.resizing = true;
8647             this.startBox = this.el.getBox();
8648             this.startPoint = e.getXY();
8649             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8650                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8651
8652             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8653             this.overlay.show();
8654
8655             if(this.constrainTo) {
8656                 var ct = Roo.get(this.constrainTo);
8657                 this.resizeRegion = ct.getRegion().adjust(
8658                     ct.getFrameWidth('t'),
8659                     ct.getFrameWidth('l'),
8660                     -ct.getFrameWidth('b'),
8661                     -ct.getFrameWidth('r')
8662                 );
8663             }
8664
8665             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8666             this.proxy.show();
8667             this.proxy.setBox(this.startBox);
8668             if(!this.dynamic){
8669                 this.proxy.setStyle('visibility', 'visible');
8670             }
8671         }
8672     },
8673
8674     // private
8675     onMouseDown : function(handle, e){
8676         if(this.enabled){
8677             e.stopEvent();
8678             this.activeHandle = handle;
8679             this.startSizing(e, handle);
8680         }
8681     },
8682
8683     // private
8684     onMouseUp : function(e){
8685         var size = this.resizeElement();
8686         this.resizing = false;
8687         this.handleOut();
8688         this.overlay.hide();
8689         this.proxy.hide();
8690         this.fireEvent("resize", this, size.width, size.height, e);
8691     },
8692
8693     // private
8694     updateChildSize : function(){
8695         
8696         if(this.resizeChild){
8697             var el = this.el;
8698             var child = this.resizeChild;
8699             var adj = this.adjustments;
8700             if(el.dom.offsetWidth){
8701                 var b = el.getSize(true);
8702                 child.setSize(b.width+adj[0], b.height+adj[1]);
8703             }
8704             // Second call here for IE
8705             // The first call enables instant resizing and
8706             // the second call corrects scroll bars if they
8707             // exist
8708             if(Roo.isIE){
8709                 setTimeout(function(){
8710                     if(el.dom.offsetWidth){
8711                         var b = el.getSize(true);
8712                         child.setSize(b.width+adj[0], b.height+adj[1]);
8713                     }
8714                 }, 10);
8715             }
8716         }
8717     },
8718
8719     // private
8720     snap : function(value, inc, min){
8721         if(!inc || !value) {
8722             return value;
8723         }
8724         var newValue = value;
8725         var m = value % inc;
8726         if(m > 0){
8727             if(m > (inc/2)){
8728                 newValue = value + (inc-m);
8729             }else{
8730                 newValue = value - m;
8731             }
8732         }
8733         return Math.max(min, newValue);
8734     },
8735
8736     // private
8737     resizeElement : function(){
8738         var box = this.proxy.getBox();
8739         if(this.updateBox){
8740             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8741         }else{
8742             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8743         }
8744         this.updateChildSize();
8745         if(!this.dynamic){
8746             this.proxy.hide();
8747         }
8748         return box;
8749     },
8750
8751     // private
8752     constrain : function(v, diff, m, mx){
8753         if(v - diff < m){
8754             diff = v - m;
8755         }else if(v - diff > mx){
8756             diff = mx - v;
8757         }
8758         return diff;
8759     },
8760
8761     // private
8762     onMouseMove : function(e){
8763         
8764         if(this.enabled){
8765             try{// try catch so if something goes wrong the user doesn't get hung
8766
8767             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8768                 return;
8769             }
8770
8771             //var curXY = this.startPoint;
8772             var curSize = this.curSize || this.startBox;
8773             var x = this.startBox.x, y = this.startBox.y;
8774             var ox = x, oy = y;
8775             var w = curSize.width, h = curSize.height;
8776             var ow = w, oh = h;
8777             var mw = this.minWidth, mh = this.minHeight;
8778             var mxw = this.maxWidth, mxh = this.maxHeight;
8779             var wi = this.widthIncrement;
8780             var hi = this.heightIncrement;
8781
8782             var eventXY = e.getXY();
8783             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8784             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8785
8786             var pos = this.activeHandle.position;
8787
8788             switch(pos){
8789                 case "east":
8790                     w += diffX;
8791                     w = Math.min(Math.max(mw, w), mxw);
8792                     break;
8793              
8794                 case "south":
8795                     h += diffY;
8796                     h = Math.min(Math.max(mh, h), mxh);
8797                     break;
8798                 case "southeast":
8799                     w += diffX;
8800                     h += diffY;
8801                     w = Math.min(Math.max(mw, w), mxw);
8802                     h = Math.min(Math.max(mh, h), mxh);
8803                     break;
8804                 case "north":
8805                     diffY = this.constrain(h, diffY, mh, mxh);
8806                     y += diffY;
8807                     h -= diffY;
8808                     break;
8809                 case "hdrag":
8810                     
8811                     if (wi) {
8812                         var adiffX = Math.abs(diffX);
8813                         var sub = (adiffX % wi); // how much 
8814                         if (sub > (wi/2)) { // far enough to snap
8815                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8816                         } else {
8817                             // remove difference.. 
8818                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8819                         }
8820                     }
8821                     x += diffX;
8822                     x = Math.max(this.minX, x);
8823                     break;
8824                 case "west":
8825                     diffX = this.constrain(w, diffX, mw, mxw);
8826                     x += diffX;
8827                     w -= diffX;
8828                     break;
8829                 case "northeast":
8830                     w += diffX;
8831                     w = Math.min(Math.max(mw, w), mxw);
8832                     diffY = this.constrain(h, diffY, mh, mxh);
8833                     y += diffY;
8834                     h -= diffY;
8835                     break;
8836                 case "northwest":
8837                     diffX = this.constrain(w, diffX, mw, mxw);
8838                     diffY = this.constrain(h, diffY, mh, mxh);
8839                     y += diffY;
8840                     h -= diffY;
8841                     x += diffX;
8842                     w -= diffX;
8843                     break;
8844                case "southwest":
8845                     diffX = this.constrain(w, diffX, mw, mxw);
8846                     h += diffY;
8847                     h = Math.min(Math.max(mh, h), mxh);
8848                     x += diffX;
8849                     w -= diffX;
8850                     break;
8851             }
8852
8853             var sw = this.snap(w, wi, mw);
8854             var sh = this.snap(h, hi, mh);
8855             if(sw != w || sh != h){
8856                 switch(pos){
8857                     case "northeast":
8858                         y -= sh - h;
8859                     break;
8860                     case "north":
8861                         y -= sh - h;
8862                         break;
8863                     case "southwest":
8864                         x -= sw - w;
8865                     break;
8866                     case "west":
8867                         x -= sw - w;
8868                         break;
8869                     case "northwest":
8870                         x -= sw - w;
8871                         y -= sh - h;
8872                     break;
8873                 }
8874                 w = sw;
8875                 h = sh;
8876             }
8877
8878             if(this.preserveRatio){
8879                 switch(pos){
8880                     case "southeast":
8881                     case "east":
8882                         h = oh * (w/ow);
8883                         h = Math.min(Math.max(mh, h), mxh);
8884                         w = ow * (h/oh);
8885                        break;
8886                     case "south":
8887                         w = ow * (h/oh);
8888                         w = Math.min(Math.max(mw, w), mxw);
8889                         h = oh * (w/ow);
8890                         break;
8891                     case "northeast":
8892                         w = ow * (h/oh);
8893                         w = Math.min(Math.max(mw, w), mxw);
8894                         h = oh * (w/ow);
8895                     break;
8896                     case "north":
8897                         var tw = w;
8898                         w = ow * (h/oh);
8899                         w = Math.min(Math.max(mw, w), mxw);
8900                         h = oh * (w/ow);
8901                         x += (tw - w) / 2;
8902                         break;
8903                     case "southwest":
8904                         h = oh * (w/ow);
8905                         h = Math.min(Math.max(mh, h), mxh);
8906                         var tw = w;
8907                         w = ow * (h/oh);
8908                         x += tw - w;
8909                         break;
8910                     case "west":
8911                         var th = h;
8912                         h = oh * (w/ow);
8913                         h = Math.min(Math.max(mh, h), mxh);
8914                         y += (th - h) / 2;
8915                         var tw = w;
8916                         w = ow * (h/oh);
8917                         x += tw - w;
8918                        break;
8919                     case "northwest":
8920                         var tw = w;
8921                         var th = h;
8922                         h = oh * (w/ow);
8923                         h = Math.min(Math.max(mh, h), mxh);
8924                         w = ow * (h/oh);
8925                         y += th - h;
8926                         x += tw - w;
8927                        break;
8928
8929                 }
8930             }
8931             if (pos == 'hdrag') {
8932                 w = ow;
8933             }
8934             this.proxy.setBounds(x, y, w, h);
8935             if(this.dynamic){
8936                 this.resizeElement();
8937             }
8938             }catch(e){}
8939         }
8940         this.fireEvent("resizing", this, x, y, w, h, e);
8941     },
8942
8943     // private
8944     handleOver : function(){
8945         if(this.enabled){
8946             this.el.addClass("x-resizable-over");
8947         }
8948     },
8949
8950     // private
8951     handleOut : function(){
8952         if(!this.resizing){
8953             this.el.removeClass("x-resizable-over");
8954         }
8955     },
8956
8957     /**
8958      * Returns the element this component is bound to.
8959      * @return {Roo.Element}
8960      */
8961     getEl : function(){
8962         return this.el;
8963     },
8964
8965     /**
8966      * Returns the resizeChild element (or null).
8967      * @return {Roo.Element}
8968      */
8969     getResizeChild : function(){
8970         return this.resizeChild;
8971     },
8972     groupHandler : function()
8973     {
8974         
8975     },
8976     /**
8977      * Destroys this resizable. If the element was wrapped and
8978      * removeEl is not true then the element remains.
8979      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8980      */
8981     destroy : function(removeEl){
8982         this.proxy.remove();
8983         if(this.overlay){
8984             this.overlay.removeAllListeners();
8985             this.overlay.remove();
8986         }
8987         var ps = Roo.Resizable.positions;
8988         for(var k in ps){
8989             if(typeof ps[k] != "function" && this[ps[k]]){
8990                 var h = this[ps[k]];
8991                 h.el.removeAllListeners();
8992                 h.el.remove();
8993             }
8994         }
8995         if(removeEl){
8996             this.el.update("");
8997             this.el.remove();
8998         }
8999     }
9000 });
9001
9002 // private
9003 // hash to map config positions to true positions
9004 Roo.Resizable.positions = {
9005     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
9006     hd: "hdrag"
9007 };
9008
9009 // private
9010 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
9011     if(!this.tpl){
9012         // only initialize the template if resizable is used
9013         var tpl = Roo.DomHelper.createTemplate(
9014             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
9015         );
9016         tpl.compile();
9017         Roo.Resizable.Handle.prototype.tpl = tpl;
9018     }
9019     this.position = pos;
9020     this.rz = rz;
9021     // show north drag fro topdra
9022     var handlepos = pos == 'hdrag' ? 'north' : pos;
9023     
9024     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
9025     if (pos == 'hdrag') {
9026         this.el.setStyle('cursor', 'pointer');
9027     }
9028     this.el.unselectable();
9029     if(transparent){
9030         this.el.setOpacity(0);
9031     }
9032     this.el.on("mousedown", this.onMouseDown, this);
9033     if(!disableTrackOver){
9034         this.el.on("mouseover", this.onMouseOver, this);
9035         this.el.on("mouseout", this.onMouseOut, this);
9036     }
9037 };
9038
9039 // private
9040 Roo.Resizable.Handle.prototype = {
9041     afterResize : function(rz){
9042         Roo.log('after?');
9043         // do nothing
9044     },
9045     // private
9046     onMouseDown : function(e){
9047         this.rz.onMouseDown(this, e);
9048     },
9049     // private
9050     onMouseOver : function(e){
9051         this.rz.handleOver(this, e);
9052     },
9053     // private
9054     onMouseOut : function(e){
9055         this.rz.handleOut(this, e);
9056     }
9057 };/*
9058  * Based on:
9059  * Ext JS Library 1.1.1
9060  * Copyright(c) 2006-2007, Ext JS, LLC.
9061  *
9062  * Originally Released Under LGPL - original licence link has changed is not relivant.
9063  *
9064  * Fork - LGPL
9065  * <script type="text/javascript">
9066  */
9067
9068 /**
9069  * @class Roo.Editor
9070  * @extends Roo.Component
9071  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
9072  * @constructor
9073  * Create a new Editor
9074  * @param {Roo.form.Field} field The Field object (or descendant)
9075  * @param {Object} config The config object
9076  */
9077 Roo.Editor = function(field, config){
9078     Roo.Editor.superclass.constructor.call(this, config);
9079     this.field = field;
9080     this.addEvents({
9081         /**
9082              * @event beforestartedit
9083              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
9084              * false from the handler of this event.
9085              * @param {Editor} this
9086              * @param {Roo.Element} boundEl The underlying element bound to this editor
9087              * @param {Mixed} value The field value being set
9088              */
9089         "beforestartedit" : true,
9090         /**
9091              * @event startedit
9092              * Fires when this editor is displayed
9093              * @param {Roo.Element} boundEl The underlying element bound to this editor
9094              * @param {Mixed} value The starting field value
9095              */
9096         "startedit" : true,
9097         /**
9098              * @event beforecomplete
9099              * Fires after a change has been made to the field, but before the change is reflected in the underlying
9100              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
9101              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
9102              * event will not fire since no edit actually occurred.
9103              * @param {Editor} this
9104              * @param {Mixed} value The current field value
9105              * @param {Mixed} startValue The original field value
9106              */
9107         "beforecomplete" : true,
9108         /**
9109              * @event complete
9110              * Fires after editing is complete and any changed value has been written to the underlying field.
9111              * @param {Editor} this
9112              * @param {Mixed} value The current field value
9113              * @param {Mixed} startValue The original field value
9114              */
9115         "complete" : true,
9116         /**
9117          * @event specialkey
9118          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
9119          * {@link Roo.EventObject#getKey} to determine which key was pressed.
9120          * @param {Roo.form.Field} this
9121          * @param {Roo.EventObject} e The event object
9122          */
9123         "specialkey" : true
9124     });
9125 };
9126
9127 Roo.extend(Roo.Editor, Roo.Component, {
9128     /**
9129      * @cfg {Boolean/String} autosize
9130      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
9131      * or "height" to adopt the height only (defaults to false)
9132      */
9133     /**
9134      * @cfg {Boolean} revertInvalid
9135      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
9136      * validation fails (defaults to true)
9137      */
9138     /**
9139      * @cfg {Boolean} ignoreNoChange
9140      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
9141      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
9142      * will never be ignored.
9143      */
9144     /**
9145      * @cfg {Boolean} hideEl
9146      * False to keep the bound element visible while the editor is displayed (defaults to true)
9147      */
9148     /**
9149      * @cfg {Mixed} value
9150      * The data value of the underlying field (defaults to "")
9151      */
9152     value : "",
9153     /**
9154      * @cfg {String} alignment
9155      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
9156      */
9157     alignment: "c-c?",
9158     /**
9159      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
9160      * for bottom-right shadow (defaults to "frame")
9161      */
9162     shadow : "frame",
9163     /**
9164      * @cfg {Boolean} constrain True to constrain the editor to the viewport
9165      */
9166     constrain : false,
9167     /**
9168      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
9169      */
9170     completeOnEnter : false,
9171     /**
9172      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
9173      */
9174     cancelOnEsc : false,
9175     /**
9176      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
9177      */
9178     updateEl : false,
9179
9180     // private
9181     onRender : function(ct, position){
9182         this.el = new Roo.Layer({
9183             shadow: this.shadow,
9184             cls: "x-editor",
9185             parentEl : ct,
9186             shim : this.shim,
9187             shadowOffset:4,
9188             id: this.id,
9189             constrain: this.constrain
9190         });
9191         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
9192         if(this.field.msgTarget != 'title'){
9193             this.field.msgTarget = 'qtip';
9194         }
9195         this.field.render(this.el);
9196         if(Roo.isGecko){
9197             this.field.el.dom.setAttribute('autocomplete', 'off');
9198         }
9199         this.field.on("specialkey", this.onSpecialKey, this);
9200         if(this.swallowKeys){
9201             this.field.el.swallowEvent(['keydown','keypress']);
9202         }
9203         this.field.show();
9204         this.field.on("blur", this.onBlur, this);
9205         if(this.field.grow){
9206             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
9207         }
9208     },
9209
9210     onSpecialKey : function(field, e)
9211     {
9212         //Roo.log('editor onSpecialKey');
9213         if(this.completeOnEnter && e.getKey() == e.ENTER){
9214             e.stopEvent();
9215             this.completeEdit();
9216             return;
9217         }
9218         // do not fire special key otherwise it might hide close the editor...
9219         if(e.getKey() == e.ENTER){    
9220             return;
9221         }
9222         if(this.cancelOnEsc && e.getKey() == e.ESC){
9223             this.cancelEdit();
9224             return;
9225         } 
9226         this.fireEvent('specialkey', field, e);
9227     
9228     },
9229
9230     /**
9231      * Starts the editing process and shows the editor.
9232      * @param {String/HTMLElement/Element} el The element to edit
9233      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
9234       * to the innerHTML of el.
9235      */
9236     startEdit : function(el, value){
9237         if(this.editing){
9238             this.completeEdit();
9239         }
9240         this.boundEl = Roo.get(el);
9241         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
9242         if(!this.rendered){
9243             this.render(this.parentEl || document.body);
9244         }
9245         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
9246             return;
9247         }
9248         this.startValue = v;
9249         this.field.setValue(v);
9250         if(this.autoSize){
9251             var sz = this.boundEl.getSize();
9252             switch(this.autoSize){
9253                 case "width":
9254                 this.setSize(sz.width,  "");
9255                 break;
9256                 case "height":
9257                 this.setSize("",  sz.height);
9258                 break;
9259                 default:
9260                 this.setSize(sz.width,  sz.height);
9261             }
9262         }
9263         this.el.alignTo(this.boundEl, this.alignment);
9264         this.editing = true;
9265         if(Roo.QuickTips){
9266             Roo.QuickTips.disable();
9267         }
9268         this.show();
9269     },
9270
9271     /**
9272      * Sets the height and width of this editor.
9273      * @param {Number} width The new width
9274      * @param {Number} height The new height
9275      */
9276     setSize : function(w, h){
9277         this.field.setSize(w, h);
9278         if(this.el){
9279             this.el.sync();
9280         }
9281     },
9282
9283     /**
9284      * Realigns the editor to the bound field based on the current alignment config value.
9285      */
9286     realign : function(){
9287         this.el.alignTo(this.boundEl, this.alignment);
9288     },
9289
9290     /**
9291      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
9292      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
9293      */
9294     completeEdit : function(remainVisible){
9295         if(!this.editing){
9296             return;
9297         }
9298         var v = this.getValue();
9299         if(this.revertInvalid !== false && !this.field.isValid()){
9300             v = this.startValue;
9301             this.cancelEdit(true);
9302         }
9303         if(String(v) === String(this.startValue) && this.ignoreNoChange){
9304             this.editing = false;
9305             this.hide();
9306             return;
9307         }
9308         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
9309             this.editing = false;
9310             if(this.updateEl && this.boundEl){
9311                 this.boundEl.update(v);
9312             }
9313             if(remainVisible !== true){
9314                 this.hide();
9315             }
9316             this.fireEvent("complete", this, v, this.startValue);
9317         }
9318     },
9319
9320     // private
9321     onShow : function(){
9322         this.el.show();
9323         if(this.hideEl !== false){
9324             this.boundEl.hide();
9325         }
9326         this.field.show();
9327         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
9328             this.fixIEFocus = true;
9329             this.deferredFocus.defer(50, this);
9330         }else{
9331             this.field.focus();
9332         }
9333         this.fireEvent("startedit", this.boundEl, this.startValue);
9334     },
9335
9336     deferredFocus : function(){
9337         if(this.editing){
9338             this.field.focus();
9339         }
9340     },
9341
9342     /**
9343      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
9344      * reverted to the original starting value.
9345      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
9346      * cancel (defaults to false)
9347      */
9348     cancelEdit : function(remainVisible){
9349         if(this.editing){
9350             this.setValue(this.startValue);
9351             if(remainVisible !== true){
9352                 this.hide();
9353             }
9354         }
9355     },
9356
9357     // private
9358     onBlur : function(){
9359         if(this.allowBlur !== true && this.editing){
9360             this.completeEdit();
9361         }
9362     },
9363
9364     // private
9365     onHide : function(){
9366         if(this.editing){
9367             this.completeEdit();
9368             return;
9369         }
9370         this.field.blur();
9371         if(this.field.collapse){
9372             this.field.collapse();
9373         }
9374         this.el.hide();
9375         if(this.hideEl !== false){
9376             this.boundEl.show();
9377         }
9378         if(Roo.QuickTips){
9379             Roo.QuickTips.enable();
9380         }
9381     },
9382
9383     /**
9384      * Sets the data value of the editor
9385      * @param {Mixed} value Any valid value supported by the underlying field
9386      */
9387     setValue : function(v){
9388         this.field.setValue(v);
9389     },
9390
9391     /**
9392      * Gets the data value of the editor
9393      * @return {Mixed} The data value
9394      */
9395     getValue : function(){
9396         return this.field.getValue();
9397     }
9398 });/*
9399  * Based on:
9400  * Ext JS Library 1.1.1
9401  * Copyright(c) 2006-2007, Ext JS, LLC.
9402  *
9403  * Originally Released Under LGPL - original licence link has changed is not relivant.
9404  *
9405  * Fork - LGPL
9406  * <script type="text/javascript">
9407  */
9408  
9409 /**
9410  * @class Roo.BasicDialog
9411  * @extends Roo.util.Observable
9412  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
9413  * <pre><code>
9414 var dlg = new Roo.BasicDialog("my-dlg", {
9415     height: 200,
9416     width: 300,
9417     minHeight: 100,
9418     minWidth: 150,
9419     modal: true,
9420     proxyDrag: true,
9421     shadow: true
9422 });
9423 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
9424 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
9425 dlg.addButton('Cancel', dlg.hide, dlg);
9426 dlg.show();
9427 </code></pre>
9428   <b>A Dialog should always be a direct child of the body element.</b>
9429  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
9430  * @cfg {String} title Default text to display in the title bar (defaults to null)
9431  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9432  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9433  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9434  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9435  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9436  * (defaults to null with no animation)
9437  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9438  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9439  * property for valid values (defaults to 'all')
9440  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9441  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9442  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9443  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9444  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9445  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9446  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9447  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9448  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9449  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9450  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9451  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9452  * draggable = true (defaults to false)
9453  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9454  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9455  * shadow (defaults to false)
9456  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9457  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9458  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9459  * @cfg {Array} buttons Array of buttons
9460  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9461  * @constructor
9462  * Create a new BasicDialog.
9463  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9464  * @param {Object} config Configuration options
9465  */
9466 Roo.BasicDialog = function(el, config){
9467     this.el = Roo.get(el);
9468     var dh = Roo.DomHelper;
9469     if(!this.el && config && config.autoCreate){
9470         if(typeof config.autoCreate == "object"){
9471             if(!config.autoCreate.id){
9472                 config.autoCreate.id = el;
9473             }
9474             this.el = dh.append(document.body,
9475                         config.autoCreate, true);
9476         }else{
9477             this.el = dh.append(document.body,
9478                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9479         }
9480     }
9481     el = this.el;
9482     el.setDisplayed(true);
9483     el.hide = this.hideAction;
9484     this.id = el.id;
9485     el.addClass("x-dlg");
9486
9487     Roo.apply(this, config);
9488
9489     this.proxy = el.createProxy("x-dlg-proxy");
9490     this.proxy.hide = this.hideAction;
9491     this.proxy.setOpacity(.5);
9492     this.proxy.hide();
9493
9494     if(config.width){
9495         el.setWidth(config.width);
9496     }
9497     if(config.height){
9498         el.setHeight(config.height);
9499     }
9500     this.size = el.getSize();
9501     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9502         this.xy = [config.x,config.y];
9503     }else{
9504         this.xy = el.getCenterXY(true);
9505     }
9506     /** The header element @type Roo.Element */
9507     this.header = el.child("> .x-dlg-hd");
9508     /** The body element @type Roo.Element */
9509     this.body = el.child("> .x-dlg-bd");
9510     /** The footer element @type Roo.Element */
9511     this.footer = el.child("> .x-dlg-ft");
9512
9513     if(!this.header){
9514         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9515     }
9516     if(!this.body){
9517         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9518     }
9519
9520     this.header.unselectable();
9521     if(this.title){
9522         this.header.update(this.title);
9523     }
9524     // this element allows the dialog to be focused for keyboard event
9525     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9526     this.focusEl.swallowEvent("click", true);
9527
9528     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9529
9530     // wrap the body and footer for special rendering
9531     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9532     if(this.footer){
9533         this.bwrap.dom.appendChild(this.footer.dom);
9534     }
9535
9536     this.bg = this.el.createChild({
9537         tag: "div", cls:"x-dlg-bg",
9538         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9539     });
9540     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9541
9542
9543     if(this.autoScroll !== false && !this.autoTabs){
9544         this.body.setStyle("overflow", "auto");
9545     }
9546
9547     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9548
9549     if(this.closable !== false){
9550         this.el.addClass("x-dlg-closable");
9551         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9552         this.close.on("click", this.closeClick, this);
9553         this.close.addClassOnOver("x-dlg-close-over");
9554     }
9555     if(this.collapsible !== false){
9556         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9557         this.collapseBtn.on("click", this.collapseClick, this);
9558         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9559         this.header.on("dblclick", this.collapseClick, this);
9560     }
9561     if(this.resizable !== false){
9562         this.el.addClass("x-dlg-resizable");
9563         this.resizer = new Roo.Resizable(el, {
9564             minWidth: this.minWidth || 80,
9565             minHeight:this.minHeight || 80,
9566             handles: this.resizeHandles || "all",
9567             pinned: true
9568         });
9569         this.resizer.on("beforeresize", this.beforeResize, this);
9570         this.resizer.on("resize", this.onResize, this);
9571     }
9572     if(this.draggable !== false){
9573         el.addClass("x-dlg-draggable");
9574         if (!this.proxyDrag) {
9575             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9576         }
9577         else {
9578             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9579         }
9580         dd.setHandleElId(this.header.id);
9581         dd.endDrag = this.endMove.createDelegate(this);
9582         dd.startDrag = this.startMove.createDelegate(this);
9583         dd.onDrag = this.onDrag.createDelegate(this);
9584         dd.scroll = false;
9585         this.dd = dd;
9586     }
9587     if(this.modal){
9588         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9589         this.mask.enableDisplayMode("block");
9590         this.mask.hide();
9591         this.el.addClass("x-dlg-modal");
9592     }
9593     if(this.shadow){
9594         this.shadow = new Roo.Shadow({
9595             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9596             offset : this.shadowOffset
9597         });
9598     }else{
9599         this.shadowOffset = 0;
9600     }
9601     if(Roo.useShims && this.shim !== false){
9602         this.shim = this.el.createShim();
9603         this.shim.hide = this.hideAction;
9604         this.shim.hide();
9605     }else{
9606         this.shim = false;
9607     }
9608     if(this.autoTabs){
9609         this.initTabs();
9610     }
9611     if (this.buttons) { 
9612         var bts= this.buttons;
9613         this.buttons = [];
9614         Roo.each(bts, function(b) {
9615             this.addButton(b);
9616         }, this);
9617     }
9618     
9619     
9620     this.addEvents({
9621         /**
9622          * @event keydown
9623          * Fires when a key is pressed
9624          * @param {Roo.BasicDialog} this
9625          * @param {Roo.EventObject} e
9626          */
9627         "keydown" : true,
9628         /**
9629          * @event move
9630          * Fires when this dialog is moved by the user.
9631          * @param {Roo.BasicDialog} this
9632          * @param {Number} x The new page X
9633          * @param {Number} y The new page Y
9634          */
9635         "move" : true,
9636         /**
9637          * @event resize
9638          * Fires when this dialog is resized by the user.
9639          * @param {Roo.BasicDialog} this
9640          * @param {Number} width The new width
9641          * @param {Number} height The new height
9642          */
9643         "resize" : true,
9644         /**
9645          * @event beforehide
9646          * Fires before this dialog is hidden.
9647          * @param {Roo.BasicDialog} this
9648          */
9649         "beforehide" : true,
9650         /**
9651          * @event hide
9652          * Fires when this dialog is hidden.
9653          * @param {Roo.BasicDialog} this
9654          */
9655         "hide" : true,
9656         /**
9657          * @event beforeshow
9658          * Fires before this dialog is shown.
9659          * @param {Roo.BasicDialog} this
9660          */
9661         "beforeshow" : true,
9662         /**
9663          * @event show
9664          * Fires when this dialog is shown.
9665          * @param {Roo.BasicDialog} this
9666          */
9667         "show" : true
9668     });
9669     el.on("keydown", this.onKeyDown, this);
9670     el.on("mousedown", this.toFront, this);
9671     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9672     this.el.hide();
9673     Roo.DialogManager.register(this);
9674     Roo.BasicDialog.superclass.constructor.call(this);
9675 };
9676
9677 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9678     shadowOffset: Roo.isIE ? 6 : 5,
9679     minHeight: 80,
9680     minWidth: 200,
9681     minButtonWidth: 75,
9682     defaultButton: null,
9683     buttonAlign: "right",
9684     tabTag: 'div',
9685     firstShow: true,
9686
9687     /**
9688      * Sets the dialog title text
9689      * @param {String} text The title text to display
9690      * @return {Roo.BasicDialog} this
9691      */
9692     setTitle : function(text){
9693         this.header.update(text);
9694         return this;
9695     },
9696
9697     // private
9698     closeClick : function(){
9699         this.hide();
9700     },
9701
9702     // private
9703     collapseClick : function(){
9704         this[this.collapsed ? "expand" : "collapse"]();
9705     },
9706
9707     /**
9708      * Collapses the dialog to its minimized state (only the title bar is visible).
9709      * Equivalent to the user clicking the collapse dialog button.
9710      */
9711     collapse : function(){
9712         if(!this.collapsed){
9713             this.collapsed = true;
9714             this.el.addClass("x-dlg-collapsed");
9715             this.restoreHeight = this.el.getHeight();
9716             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9717         }
9718     },
9719
9720     /**
9721      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9722      * clicking the expand dialog button.
9723      */
9724     expand : function(){
9725         if(this.collapsed){
9726             this.collapsed = false;
9727             this.el.removeClass("x-dlg-collapsed");
9728             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9729         }
9730     },
9731
9732     /**
9733      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9734      * @return {Roo.TabPanel} The tabs component
9735      */
9736     initTabs : function(){
9737         var tabs = this.getTabs();
9738         while(tabs.getTab(0)){
9739             tabs.removeTab(0);
9740         }
9741         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9742             var dom = el.dom;
9743             tabs.addTab(Roo.id(dom), dom.title);
9744             dom.title = "";
9745         });
9746         tabs.activate(0);
9747         return tabs;
9748     },
9749
9750     // private
9751     beforeResize : function(){
9752         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9753     },
9754
9755     // private
9756     onResize : function(){
9757         this.refreshSize();
9758         this.syncBodyHeight();
9759         this.adjustAssets();
9760         this.focus();
9761         this.fireEvent("resize", this, this.size.width, this.size.height);
9762     },
9763
9764     // private
9765     onKeyDown : function(e){
9766         if(this.isVisible()){
9767             this.fireEvent("keydown", this, e);
9768         }
9769     },
9770
9771     /**
9772      * Resizes the dialog.
9773      * @param {Number} width
9774      * @param {Number} height
9775      * @return {Roo.BasicDialog} this
9776      */
9777     resizeTo : function(width, height){
9778         this.el.setSize(width, height);
9779         this.size = {width: width, height: height};
9780         this.syncBodyHeight();
9781         if(this.fixedcenter){
9782             this.center();
9783         }
9784         if(this.isVisible()){
9785             this.constrainXY();
9786             this.adjustAssets();
9787         }
9788         this.fireEvent("resize", this, width, height);
9789         return this;
9790     },
9791
9792
9793     /**
9794      * Resizes the dialog to fit the specified content size.
9795      * @param {Number} width
9796      * @param {Number} height
9797      * @return {Roo.BasicDialog} this
9798      */
9799     setContentSize : function(w, h){
9800         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9801         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9802         //if(!this.el.isBorderBox()){
9803             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9804             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9805         //}
9806         if(this.tabs){
9807             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9808             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9809         }
9810         this.resizeTo(w, h);
9811         return this;
9812     },
9813
9814     /**
9815      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9816      * executed in response to a particular key being pressed while the dialog is active.
9817      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9818      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9819      * @param {Function} fn The function to call
9820      * @param {Object} scope (optional) The scope of the function
9821      * @return {Roo.BasicDialog} this
9822      */
9823     addKeyListener : function(key, fn, scope){
9824         var keyCode, shift, ctrl, alt;
9825         if(typeof key == "object" && !(key instanceof Array)){
9826             keyCode = key["key"];
9827             shift = key["shift"];
9828             ctrl = key["ctrl"];
9829             alt = key["alt"];
9830         }else{
9831             keyCode = key;
9832         }
9833         var handler = function(dlg, e){
9834             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9835                 var k = e.getKey();
9836                 if(keyCode instanceof Array){
9837                     for(var i = 0, len = keyCode.length; i < len; i++){
9838                         if(keyCode[i] == k){
9839                           fn.call(scope || window, dlg, k, e);
9840                           return;
9841                         }
9842                     }
9843                 }else{
9844                     if(k == keyCode){
9845                         fn.call(scope || window, dlg, k, e);
9846                     }
9847                 }
9848             }
9849         };
9850         this.on("keydown", handler);
9851         return this;
9852     },
9853
9854     /**
9855      * Returns the TabPanel component (creates it if it doesn't exist).
9856      * Note: If you wish to simply check for the existence of tabs without creating them,
9857      * check for a null 'tabs' property.
9858      * @return {Roo.TabPanel} The tabs component
9859      */
9860     getTabs : function(){
9861         if(!this.tabs){
9862             this.el.addClass("x-dlg-auto-tabs");
9863             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9864             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9865         }
9866         return this.tabs;
9867     },
9868
9869     /**
9870      * Adds a button to the footer section of the dialog.
9871      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9872      * object or a valid Roo.DomHelper element config
9873      * @param {Function} handler The function called when the button is clicked
9874      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9875      * @return {Roo.Button} The new button
9876      */
9877     addButton : function(config, handler, scope){
9878         var dh = Roo.DomHelper;
9879         if(!this.footer){
9880             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9881         }
9882         if(!this.btnContainer){
9883             var tb = this.footer.createChild({
9884
9885                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9886                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9887             }, null, true);
9888             this.btnContainer = tb.firstChild.firstChild.firstChild;
9889         }
9890         var bconfig = {
9891             handler: handler,
9892             scope: scope,
9893             minWidth: this.minButtonWidth,
9894             hideParent:true
9895         };
9896         if(typeof config == "string"){
9897             bconfig.text = config;
9898         }else{
9899             if(config.tag){
9900                 bconfig.dhconfig = config;
9901             }else{
9902                 Roo.apply(bconfig, config);
9903             }
9904         }
9905         var fc = false;
9906         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9907             bconfig.position = Math.max(0, bconfig.position);
9908             fc = this.btnContainer.childNodes[bconfig.position];
9909         }
9910          
9911         var btn = new Roo.Button(
9912             fc ? 
9913                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9914                 : this.btnContainer.appendChild(document.createElement("td")),
9915             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9916             bconfig
9917         );
9918         this.syncBodyHeight();
9919         if(!this.buttons){
9920             /**
9921              * Array of all the buttons that have been added to this dialog via addButton
9922              * @type Array
9923              */
9924             this.buttons = [];
9925         }
9926         this.buttons.push(btn);
9927         return btn;
9928     },
9929
9930     /**
9931      * Sets the default button to be focused when the dialog is displayed.
9932      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9933      * @return {Roo.BasicDialog} this
9934      */
9935     setDefaultButton : function(btn){
9936         this.defaultButton = btn;
9937         return this;
9938     },
9939
9940     // private
9941     getHeaderFooterHeight : function(safe){
9942         var height = 0;
9943         if(this.header){
9944            height += this.header.getHeight();
9945         }
9946         if(this.footer){
9947            var fm = this.footer.getMargins();
9948             height += (this.footer.getHeight()+fm.top+fm.bottom);
9949         }
9950         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9951         height += this.centerBg.getPadding("tb");
9952         return height;
9953     },
9954
9955     // private
9956     syncBodyHeight : function()
9957     {
9958         var bd = this.body, // the text
9959             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9960             bw = this.bwrap;
9961         var height = this.size.height - this.getHeaderFooterHeight(false);
9962         bd.setHeight(height-bd.getMargins("tb"));
9963         var hh = this.header.getHeight();
9964         var h = this.size.height-hh;
9965         cb.setHeight(h);
9966         
9967         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9968         bw.setHeight(h-cb.getPadding("tb"));
9969         
9970         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9971         bd.setWidth(bw.getWidth(true));
9972         if(this.tabs){
9973             this.tabs.syncHeight();
9974             if(Roo.isIE){
9975                 this.tabs.el.repaint();
9976             }
9977         }
9978     },
9979
9980     /**
9981      * Restores the previous state of the dialog if Roo.state is configured.
9982      * @return {Roo.BasicDialog} this
9983      */
9984     restoreState : function(){
9985         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9986         if(box && box.width){
9987             this.xy = [box.x, box.y];
9988             this.resizeTo(box.width, box.height);
9989         }
9990         return this;
9991     },
9992
9993     // private
9994     beforeShow : function(){
9995         this.expand();
9996         if(this.fixedcenter){
9997             this.xy = this.el.getCenterXY(true);
9998         }
9999         if(this.modal){
10000             Roo.get(document.body).addClass("x-body-masked");
10001             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10002             this.mask.show();
10003         }
10004         this.constrainXY();
10005     },
10006
10007     // private
10008     animShow : function(){
10009         var b = Roo.get(this.animateTarget).getBox();
10010         this.proxy.setSize(b.width, b.height);
10011         this.proxy.setLocation(b.x, b.y);
10012         this.proxy.show();
10013         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
10014                     true, .35, this.showEl.createDelegate(this));
10015     },
10016
10017     /**
10018      * Shows the dialog.
10019      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
10020      * @return {Roo.BasicDialog} this
10021      */
10022     show : function(animateTarget){
10023         if (this.fireEvent("beforeshow", this) === false){
10024             return;
10025         }
10026         if(this.syncHeightBeforeShow){
10027             this.syncBodyHeight();
10028         }else if(this.firstShow){
10029             this.firstShow = false;
10030             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
10031         }
10032         this.animateTarget = animateTarget || this.animateTarget;
10033         if(!this.el.isVisible()){
10034             this.beforeShow();
10035             if(this.animateTarget && Roo.get(this.animateTarget)){
10036                 this.animShow();
10037             }else{
10038                 this.showEl();
10039             }
10040         }
10041         return this;
10042     },
10043
10044     // private
10045     showEl : function(){
10046         this.proxy.hide();
10047         this.el.setXY(this.xy);
10048         this.el.show();
10049         this.adjustAssets(true);
10050         this.toFront();
10051         this.focus();
10052         // IE peekaboo bug - fix found by Dave Fenwick
10053         if(Roo.isIE){
10054             this.el.repaint();
10055         }
10056         this.fireEvent("show", this);
10057     },
10058
10059     /**
10060      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
10061      * dialog itself will receive focus.
10062      */
10063     focus : function(){
10064         if(this.defaultButton){
10065             this.defaultButton.focus();
10066         }else{
10067             this.focusEl.focus();
10068         }
10069     },
10070
10071     // private
10072     constrainXY : function(){
10073         if(this.constraintoviewport !== false){
10074             if(!this.viewSize){
10075                 if(this.container){
10076                     var s = this.container.getSize();
10077                     this.viewSize = [s.width, s.height];
10078                 }else{
10079                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
10080                 }
10081             }
10082             var s = Roo.get(this.container||document).getScroll();
10083
10084             var x = this.xy[0], y = this.xy[1];
10085             var w = this.size.width, h = this.size.height;
10086             var vw = this.viewSize[0], vh = this.viewSize[1];
10087             // only move it if it needs it
10088             var moved = false;
10089             // first validate right/bottom
10090             if(x + w > vw+s.left){
10091                 x = vw - w;
10092                 moved = true;
10093             }
10094             if(y + h > vh+s.top){
10095                 y = vh - h;
10096                 moved = true;
10097             }
10098             // then make sure top/left isn't negative
10099             if(x < s.left){
10100                 x = s.left;
10101                 moved = true;
10102             }
10103             if(y < s.top){
10104                 y = s.top;
10105                 moved = true;
10106             }
10107             if(moved){
10108                 // cache xy
10109                 this.xy = [x, y];
10110                 if(this.isVisible()){
10111                     this.el.setLocation(x, y);
10112                     this.adjustAssets();
10113                 }
10114             }
10115         }
10116     },
10117
10118     // private
10119     onDrag : function(){
10120         if(!this.proxyDrag){
10121             this.xy = this.el.getXY();
10122             this.adjustAssets();
10123         }
10124     },
10125
10126     // private
10127     adjustAssets : function(doShow){
10128         var x = this.xy[0], y = this.xy[1];
10129         var w = this.size.width, h = this.size.height;
10130         if(doShow === true){
10131             if(this.shadow){
10132                 this.shadow.show(this.el);
10133             }
10134             if(this.shim){
10135                 this.shim.show();
10136             }
10137         }
10138         if(this.shadow && this.shadow.isVisible()){
10139             this.shadow.show(this.el);
10140         }
10141         if(this.shim && this.shim.isVisible()){
10142             this.shim.setBounds(x, y, w, h);
10143         }
10144     },
10145
10146     // private
10147     adjustViewport : function(w, h){
10148         if(!w || !h){
10149             w = Roo.lib.Dom.getViewWidth();
10150             h = Roo.lib.Dom.getViewHeight();
10151         }
10152         // cache the size
10153         this.viewSize = [w, h];
10154         if(this.modal && this.mask.isVisible()){
10155             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
10156             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
10157         }
10158         if(this.isVisible()){
10159             this.constrainXY();
10160         }
10161     },
10162
10163     /**
10164      * Destroys this dialog and all its supporting elements (including any tabs, shim,
10165      * shadow, proxy, mask, etc.)  Also removes all event listeners.
10166      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
10167      */
10168     destroy : function(removeEl){
10169         if(this.isVisible()){
10170             this.animateTarget = null;
10171             this.hide();
10172         }
10173         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
10174         if(this.tabs){
10175             this.tabs.destroy(removeEl);
10176         }
10177         Roo.destroy(
10178              this.shim,
10179              this.proxy,
10180              this.resizer,
10181              this.close,
10182              this.mask
10183         );
10184         if(this.dd){
10185             this.dd.unreg();
10186         }
10187         if(this.buttons){
10188            for(var i = 0, len = this.buttons.length; i < len; i++){
10189                this.buttons[i].destroy();
10190            }
10191         }
10192         this.el.removeAllListeners();
10193         if(removeEl === true){
10194             this.el.update("");
10195             this.el.remove();
10196         }
10197         Roo.DialogManager.unregister(this);
10198     },
10199
10200     // private
10201     startMove : function(){
10202         if(this.proxyDrag){
10203             this.proxy.show();
10204         }
10205         if(this.constraintoviewport !== false){
10206             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
10207         }
10208     },
10209
10210     // private
10211     endMove : function(){
10212         if(!this.proxyDrag){
10213             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
10214         }else{
10215             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
10216             this.proxy.hide();
10217         }
10218         this.refreshSize();
10219         this.adjustAssets();
10220         this.focus();
10221         this.fireEvent("move", this, this.xy[0], this.xy[1]);
10222     },
10223
10224     /**
10225      * Brings this dialog to the front of any other visible dialogs
10226      * @return {Roo.BasicDialog} this
10227      */
10228     toFront : function(){
10229         Roo.DialogManager.bringToFront(this);
10230         return this;
10231     },
10232
10233     /**
10234      * Sends this dialog to the back (under) of any other visible dialogs
10235      * @return {Roo.BasicDialog} this
10236      */
10237     toBack : function(){
10238         Roo.DialogManager.sendToBack(this);
10239         return this;
10240     },
10241
10242     /**
10243      * Centers this dialog in the viewport
10244      * @return {Roo.BasicDialog} this
10245      */
10246     center : function(){
10247         var xy = this.el.getCenterXY(true);
10248         this.moveTo(xy[0], xy[1]);
10249         return this;
10250     },
10251
10252     /**
10253      * Moves the dialog's top-left corner to the specified point
10254      * @param {Number} x
10255      * @param {Number} y
10256      * @return {Roo.BasicDialog} this
10257      */
10258     moveTo : function(x, y){
10259         this.xy = [x,y];
10260         if(this.isVisible()){
10261             this.el.setXY(this.xy);
10262             this.adjustAssets();
10263         }
10264         return this;
10265     },
10266
10267     /**
10268      * Aligns the dialog to the specified element
10269      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10270      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
10271      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10272      * @return {Roo.BasicDialog} this
10273      */
10274     alignTo : function(element, position, offsets){
10275         this.xy = this.el.getAlignToXY(element, position, offsets);
10276         if(this.isVisible()){
10277             this.el.setXY(this.xy);
10278             this.adjustAssets();
10279         }
10280         return this;
10281     },
10282
10283     /**
10284      * Anchors an element to another element and realigns it when the window is resized.
10285      * @param {String/HTMLElement/Roo.Element} element The element to align to.
10286      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
10287      * @param {Array} offsets (optional) Offset the positioning by [x, y]
10288      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
10289      * is a number, it is used as the buffer delay (defaults to 50ms).
10290      * @return {Roo.BasicDialog} this
10291      */
10292     anchorTo : function(el, alignment, offsets, monitorScroll){
10293         var action = function(){
10294             this.alignTo(el, alignment, offsets);
10295         };
10296         Roo.EventManager.onWindowResize(action, this);
10297         var tm = typeof monitorScroll;
10298         if(tm != 'undefined'){
10299             Roo.EventManager.on(window, 'scroll', action, this,
10300                 {buffer: tm == 'number' ? monitorScroll : 50});
10301         }
10302         action.call(this);
10303         return this;
10304     },
10305
10306     /**
10307      * Returns true if the dialog is visible
10308      * @return {Boolean}
10309      */
10310     isVisible : function(){
10311         return this.el.isVisible();
10312     },
10313
10314     // private
10315     animHide : function(callback){
10316         var b = Roo.get(this.animateTarget).getBox();
10317         this.proxy.show();
10318         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
10319         this.el.hide();
10320         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
10321                     this.hideEl.createDelegate(this, [callback]));
10322     },
10323
10324     /**
10325      * Hides the dialog.
10326      * @param {Function} callback (optional) Function to call when the dialog is hidden
10327      * @return {Roo.BasicDialog} this
10328      */
10329     hide : function(callback){
10330         if (this.fireEvent("beforehide", this) === false){
10331             return;
10332         }
10333         if(this.shadow){
10334             this.shadow.hide();
10335         }
10336         if(this.shim) {
10337           this.shim.hide();
10338         }
10339         // sometimes animateTarget seems to get set.. causing problems...
10340         // this just double checks..
10341         if(this.animateTarget && Roo.get(this.animateTarget)) {
10342            this.animHide(callback);
10343         }else{
10344             this.el.hide();
10345             this.hideEl(callback);
10346         }
10347         return this;
10348     },
10349
10350     // private
10351     hideEl : function(callback){
10352         this.proxy.hide();
10353         if(this.modal){
10354             this.mask.hide();
10355             Roo.get(document.body).removeClass("x-body-masked");
10356         }
10357         this.fireEvent("hide", this);
10358         if(typeof callback == "function"){
10359             callback();
10360         }
10361     },
10362
10363     // private
10364     hideAction : function(){
10365         this.setLeft("-10000px");
10366         this.setTop("-10000px");
10367         this.setStyle("visibility", "hidden");
10368     },
10369
10370     // private
10371     refreshSize : function(){
10372         this.size = this.el.getSize();
10373         this.xy = this.el.getXY();
10374         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
10375     },
10376
10377     // private
10378     // z-index is managed by the DialogManager and may be overwritten at any time
10379     setZIndex : function(index){
10380         if(this.modal){
10381             this.mask.setStyle("z-index", index);
10382         }
10383         if(this.shim){
10384             this.shim.setStyle("z-index", ++index);
10385         }
10386         if(this.shadow){
10387             this.shadow.setZIndex(++index);
10388         }
10389         this.el.setStyle("z-index", ++index);
10390         if(this.proxy){
10391             this.proxy.setStyle("z-index", ++index);
10392         }
10393         if(this.resizer){
10394             this.resizer.proxy.setStyle("z-index", ++index);
10395         }
10396
10397         this.lastZIndex = index;
10398     },
10399
10400     /**
10401      * Returns the element for this dialog
10402      * @return {Roo.Element} The underlying dialog Element
10403      */
10404     getEl : function(){
10405         return this.el;
10406     }
10407 });
10408
10409 /**
10410  * @class Roo.DialogManager
10411  * Provides global access to BasicDialogs that have been created and
10412  * support for z-indexing (layering) multiple open dialogs.
10413  */
10414 Roo.DialogManager = function(){
10415     var list = {};
10416     var accessList = [];
10417     var front = null;
10418
10419     // private
10420     var sortDialogs = function(d1, d2){
10421         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
10422     };
10423
10424     // private
10425     var orderDialogs = function(){
10426         accessList.sort(sortDialogs);
10427         var seed = Roo.DialogManager.zseed;
10428         for(var i = 0, len = accessList.length; i < len; i++){
10429             var dlg = accessList[i];
10430             if(dlg){
10431                 dlg.setZIndex(seed + (i*10));
10432             }
10433         }
10434     };
10435
10436     return {
10437         /**
10438          * The starting z-index for BasicDialogs (defaults to 9000)
10439          * @type Number The z-index value
10440          */
10441         zseed : 9000,
10442
10443         // private
10444         register : function(dlg){
10445             list[dlg.id] = dlg;
10446             accessList.push(dlg);
10447         },
10448
10449         // private
10450         unregister : function(dlg){
10451             delete list[dlg.id];
10452             var i=0;
10453             var len=0;
10454             if(!accessList.indexOf){
10455                 for(  i = 0, len = accessList.length; i < len; i++){
10456                     if(accessList[i] == dlg){
10457                         accessList.splice(i, 1);
10458                         return;
10459                     }
10460                 }
10461             }else{
10462                  i = accessList.indexOf(dlg);
10463                 if(i != -1){
10464                     accessList.splice(i, 1);
10465                 }
10466             }
10467         },
10468
10469         /**
10470          * Gets a registered dialog by id
10471          * @param {String/Object} id The id of the dialog or a dialog
10472          * @return {Roo.BasicDialog} this
10473          */
10474         get : function(id){
10475             return typeof id == "object" ? id : list[id];
10476         },
10477
10478         /**
10479          * Brings the specified dialog to the front
10480          * @param {String/Object} dlg The id of the dialog or a dialog
10481          * @return {Roo.BasicDialog} this
10482          */
10483         bringToFront : function(dlg){
10484             dlg = this.get(dlg);
10485             if(dlg != front){
10486                 front = dlg;
10487                 dlg._lastAccess = new Date().getTime();
10488                 orderDialogs();
10489             }
10490             return dlg;
10491         },
10492
10493         /**
10494          * Sends the specified dialog to the back
10495          * @param {String/Object} dlg The id of the dialog or a dialog
10496          * @return {Roo.BasicDialog} this
10497          */
10498         sendToBack : function(dlg){
10499             dlg = this.get(dlg);
10500             dlg._lastAccess = -(new Date().getTime());
10501             orderDialogs();
10502             return dlg;
10503         },
10504
10505         /**
10506          * Hides all dialogs
10507          */
10508         hideAll : function(){
10509             for(var id in list){
10510                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10511                     list[id].hide();
10512                 }
10513             }
10514         }
10515     };
10516 }();
10517
10518 /**
10519  * @class Roo.LayoutDialog
10520  * @extends Roo.BasicDialog
10521  * Dialog which provides adjustments for working with a layout in a Dialog.
10522  * Add your necessary layout config options to the dialog's config.<br>
10523  * Example usage (including a nested layout):
10524  * <pre><code>
10525 if(!dialog){
10526     dialog = new Roo.LayoutDialog("download-dlg", {
10527         modal: true,
10528         width:600,
10529         height:450,
10530         shadow:true,
10531         minWidth:500,
10532         minHeight:350,
10533         autoTabs:true,
10534         proxyDrag:true,
10535         // layout config merges with the dialog config
10536         center:{
10537             tabPosition: "top",
10538             alwaysShowTabs: true
10539         }
10540     });
10541     dialog.addKeyListener(27, dialog.hide, dialog);
10542     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10543     dialog.addButton("Build It!", this.getDownload, this);
10544
10545     // we can even add nested layouts
10546     var innerLayout = new Roo.BorderLayout("dl-inner", {
10547         east: {
10548             initialSize: 200,
10549             autoScroll:true,
10550             split:true
10551         },
10552         center: {
10553             autoScroll:true
10554         }
10555     });
10556     innerLayout.beginUpdate();
10557     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10558     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10559     innerLayout.endUpdate(true);
10560
10561     var layout = dialog.getLayout();
10562     layout.beginUpdate();
10563     layout.add("center", new Roo.ContentPanel("standard-panel",
10564                         {title: "Download the Source", fitToFrame:true}));
10565     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10566                {title: "Build your own roo.js"}));
10567     layout.getRegion("center").showPanel(sp);
10568     layout.endUpdate();
10569 }
10570 </code></pre>
10571     * @constructor
10572     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10573     * @param {Object} config configuration options
10574   */
10575 Roo.LayoutDialog = function(el, cfg){
10576     
10577     var config=  cfg;
10578     if (typeof(cfg) == 'undefined') {
10579         config = Roo.apply({}, el);
10580         // not sure why we use documentElement here.. - it should always be body.
10581         // IE7 borks horribly if we use documentElement.
10582         // webkit also does not like documentElement - it creates a body element...
10583         el = Roo.get( document.body || document.documentElement ).createChild();
10584         //config.autoCreate = true;
10585     }
10586     
10587     
10588     config.autoTabs = false;
10589     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10590     this.body.setStyle({overflow:"hidden", position:"relative"});
10591     this.layout = new Roo.BorderLayout(this.body.dom, config);
10592     this.layout.monitorWindowResize = false;
10593     this.el.addClass("x-dlg-auto-layout");
10594     // fix case when center region overwrites center function
10595     this.center = Roo.BasicDialog.prototype.center;
10596     this.on("show", this.layout.layout, this.layout, true);
10597     if (config.items) {
10598         var xitems = config.items;
10599         delete config.items;
10600         Roo.each(xitems, this.addxtype, this);
10601     }
10602     
10603     
10604 };
10605 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10606     /**
10607      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10608      * @deprecated
10609      */
10610     endUpdate : function(){
10611         this.layout.endUpdate();
10612     },
10613
10614     /**
10615      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10616      *  @deprecated
10617      */
10618     beginUpdate : function(){
10619         this.layout.beginUpdate();
10620     },
10621
10622     /**
10623      * Get the BorderLayout for this dialog
10624      * @return {Roo.BorderLayout}
10625      */
10626     getLayout : function(){
10627         return this.layout;
10628     },
10629
10630     showEl : function(){
10631         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10632         if(Roo.isIE7){
10633             this.layout.layout();
10634         }
10635     },
10636
10637     // private
10638     // Use the syncHeightBeforeShow config option to control this automatically
10639     syncBodyHeight : function(){
10640         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10641         if(this.layout){this.layout.layout();}
10642     },
10643     
10644       /**
10645      * Add an xtype element (actually adds to the layout.)
10646      * @return {Object} xdata xtype object data.
10647      */
10648     
10649     addxtype : function(c) {
10650         return this.layout.addxtype(c);
10651     }
10652 });/*
10653  * Based on:
10654  * Ext JS Library 1.1.1
10655  * Copyright(c) 2006-2007, Ext JS, LLC.
10656  *
10657  * Originally Released Under LGPL - original licence link has changed is not relivant.
10658  *
10659  * Fork - LGPL
10660  * <script type="text/javascript">
10661  */
10662  
10663 /**
10664  * @class Roo.MessageBox
10665  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10666  * Example usage:
10667  *<pre><code>
10668 // Basic alert:
10669 Roo.Msg.alert('Status', 'Changes saved successfully.');
10670
10671 // Prompt for user data:
10672 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10673     if (btn == 'ok'){
10674         // process text value...
10675     }
10676 });
10677
10678 // Show a dialog using config options:
10679 Roo.Msg.show({
10680    title:'Save Changes?',
10681    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10682    buttons: Roo.Msg.YESNOCANCEL,
10683    fn: processResult,
10684    animEl: 'elId'
10685 });
10686 </code></pre>
10687  * @singleton
10688  */
10689 Roo.MessageBox = function(){
10690     var dlg, opt, mask, waitTimer;
10691     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10692     var buttons, activeTextEl, bwidth;
10693
10694     // private
10695     var handleButton = function(button){
10696         dlg.hide();
10697         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10698     };
10699
10700     // private
10701     var handleHide = function(){
10702         if(opt && opt.cls){
10703             dlg.el.removeClass(opt.cls);
10704         }
10705         if(waitTimer){
10706             Roo.TaskMgr.stop(waitTimer);
10707             waitTimer = null;
10708         }
10709     };
10710
10711     // private
10712     var updateButtons = function(b){
10713         var width = 0;
10714         if(!b){
10715             buttons["ok"].hide();
10716             buttons["cancel"].hide();
10717             buttons["yes"].hide();
10718             buttons["no"].hide();
10719             dlg.footer.dom.style.display = 'none';
10720             return width;
10721         }
10722         dlg.footer.dom.style.display = '';
10723         for(var k in buttons){
10724             if(typeof buttons[k] != "function"){
10725                 if(b[k]){
10726                     buttons[k].show();
10727                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10728                     width += buttons[k].el.getWidth()+15;
10729                 }else{
10730                     buttons[k].hide();
10731                 }
10732             }
10733         }
10734         return width;
10735     };
10736
10737     // private
10738     var handleEsc = function(d, k, e){
10739         if(opt && opt.closable !== false){
10740             dlg.hide();
10741         }
10742         if(e){
10743             e.stopEvent();
10744         }
10745     };
10746
10747     return {
10748         /**
10749          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10750          * @return {Roo.BasicDialog} The BasicDialog element
10751          */
10752         getDialog : function(){
10753            if(!dlg){
10754                 dlg = new Roo.BasicDialog("x-msg-box", {
10755                     autoCreate : true,
10756                     shadow: true,
10757                     draggable: true,
10758                     resizable:false,
10759                     constraintoviewport:false,
10760                     fixedcenter:true,
10761                     collapsible : false,
10762                     shim:true,
10763                     modal: true,
10764                     width:400, height:100,
10765                     buttonAlign:"center",
10766                     closeClick : function(){
10767                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10768                             handleButton("no");
10769                         }else{
10770                             handleButton("cancel");
10771                         }
10772                     }
10773                 });
10774                 dlg.on("hide", handleHide);
10775                 mask = dlg.mask;
10776                 dlg.addKeyListener(27, handleEsc);
10777                 buttons = {};
10778                 var bt = this.buttonText;
10779                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10780                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10781                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10782                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10783                 bodyEl = dlg.body.createChild({
10784
10785                     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>'
10786                 });
10787                 msgEl = bodyEl.dom.firstChild;
10788                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10789                 textboxEl.enableDisplayMode();
10790                 textboxEl.addKeyListener([10,13], function(){
10791                     if(dlg.isVisible() && opt && opt.buttons){
10792                         if(opt.buttons.ok){
10793                             handleButton("ok");
10794                         }else if(opt.buttons.yes){
10795                             handleButton("yes");
10796                         }
10797                     }
10798                 });
10799                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10800                 textareaEl.enableDisplayMode();
10801                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10802                 progressEl.enableDisplayMode();
10803                 var pf = progressEl.dom.firstChild;
10804                 if (pf) {
10805                     pp = Roo.get(pf.firstChild);
10806                     pp.setHeight(pf.offsetHeight);
10807                 }
10808                 
10809             }
10810             return dlg;
10811         },
10812
10813         /**
10814          * Updates the message box body text
10815          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10816          * the XHTML-compliant non-breaking space character '&amp;#160;')
10817          * @return {Roo.MessageBox} This message box
10818          */
10819         updateText : function(text){
10820             if(!dlg.isVisible() && !opt.width){
10821                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10822             }
10823             msgEl.innerHTML = text || '&#160;';
10824       
10825             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10826             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10827             var w = Math.max(
10828                     Math.min(opt.width || cw , this.maxWidth), 
10829                     Math.max(opt.minWidth || this.minWidth, bwidth)
10830             );
10831             if(opt.prompt){
10832                 activeTextEl.setWidth(w);
10833             }
10834             if(dlg.isVisible()){
10835                 dlg.fixedcenter = false;
10836             }
10837             // to big, make it scroll. = But as usual stupid IE does not support
10838             // !important..
10839             
10840             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10841                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10842                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10843             } else {
10844                 bodyEl.dom.style.height = '';
10845                 bodyEl.dom.style.overflowY = '';
10846             }
10847             if (cw > w) {
10848                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10849             } else {
10850                 bodyEl.dom.style.overflowX = '';
10851             }
10852             
10853             dlg.setContentSize(w, bodyEl.getHeight());
10854             if(dlg.isVisible()){
10855                 dlg.fixedcenter = true;
10856             }
10857             return this;
10858         },
10859
10860         /**
10861          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10862          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10863          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10864          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10865          * @return {Roo.MessageBox} This message box
10866          */
10867         updateProgress : function(value, text){
10868             if(text){
10869                 this.updateText(text);
10870             }
10871             if (pp) { // weird bug on my firefox - for some reason this is not defined
10872                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10873             }
10874             return this;
10875         },        
10876
10877         /**
10878          * Returns true if the message box is currently displayed
10879          * @return {Boolean} True if the message box is visible, else false
10880          */
10881         isVisible : function(){
10882             return dlg && dlg.isVisible();  
10883         },
10884
10885         /**
10886          * Hides the message box if it is displayed
10887          */
10888         hide : function(){
10889             if(this.isVisible()){
10890                 dlg.hide();
10891             }  
10892         },
10893
10894         /**
10895          * Displays a new message box, or reinitializes an existing message box, based on the config options
10896          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10897          * The following config object properties are supported:
10898          * <pre>
10899 Property    Type             Description
10900 ----------  ---------------  ------------------------------------------------------------------------------------
10901 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10902                                    closes (defaults to undefined)
10903 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10904                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10905 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10906                                    progress and wait dialogs will ignore this property and always hide the
10907                                    close button as they can only be closed programmatically.
10908 cls               String           A custom CSS class to apply to the message box element
10909 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10910                                    displayed (defaults to 75)
10911 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10912                                    function will be btn (the name of the button that was clicked, if applicable,
10913                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10914                                    Progress and wait dialogs will ignore this option since they do not respond to
10915                                    user actions and can only be closed programmatically, so any required function
10916                                    should be called by the same code after it closes the dialog.
10917 icon              String           A CSS class that provides a background image to be used as an icon for
10918                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10919 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10920 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10921 modal             Boolean          False to allow user interaction with the page while the message box is
10922                                    displayed (defaults to true)
10923 msg               String           A string that will replace the existing message box body text (defaults
10924                                    to the XHTML-compliant non-breaking space character '&#160;')
10925 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10926 progress          Boolean          True to display a progress bar (defaults to false)
10927 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10928 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10929 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10930 title             String           The title text
10931 value             String           The string value to set into the active textbox element if displayed
10932 wait              Boolean          True to display a progress bar (defaults to false)
10933 width             Number           The width of the dialog in pixels
10934 </pre>
10935          *
10936          * Example usage:
10937          * <pre><code>
10938 Roo.Msg.show({
10939    title: 'Address',
10940    msg: 'Please enter your address:',
10941    width: 300,
10942    buttons: Roo.MessageBox.OKCANCEL,
10943    multiline: true,
10944    fn: saveAddress,
10945    animEl: 'addAddressBtn'
10946 });
10947 </code></pre>
10948          * @param {Object} config Configuration options
10949          * @return {Roo.MessageBox} This message box
10950          */
10951         show : function(options)
10952         {
10953             
10954             // this causes nightmares if you show one dialog after another
10955             // especially on callbacks..
10956              
10957             if(this.isVisible()){
10958                 
10959                 this.hide();
10960                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10961                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10962                 Roo.log("New Dialog Message:" +  options.msg )
10963                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10964                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10965                 
10966             }
10967             var d = this.getDialog();
10968             opt = options;
10969             d.setTitle(opt.title || "&#160;");
10970             d.close.setDisplayed(opt.closable !== false);
10971             activeTextEl = textboxEl;
10972             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10973             if(opt.prompt){
10974                 if(opt.multiline){
10975                     textboxEl.hide();
10976                     textareaEl.show();
10977                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10978                         opt.multiline : this.defaultTextHeight);
10979                     activeTextEl = textareaEl;
10980                 }else{
10981                     textboxEl.show();
10982                     textareaEl.hide();
10983                 }
10984             }else{
10985                 textboxEl.hide();
10986                 textareaEl.hide();
10987             }
10988             progressEl.setDisplayed(opt.progress === true);
10989             this.updateProgress(0);
10990             activeTextEl.dom.value = opt.value || "";
10991             if(opt.prompt){
10992                 dlg.setDefaultButton(activeTextEl);
10993             }else{
10994                 var bs = opt.buttons;
10995                 var db = null;
10996                 if(bs && bs.ok){
10997                     db = buttons["ok"];
10998                 }else if(bs && bs.yes){
10999                     db = buttons["yes"];
11000                 }
11001                 dlg.setDefaultButton(db);
11002             }
11003             bwidth = updateButtons(opt.buttons);
11004             this.updateText(opt.msg);
11005             if(opt.cls){
11006                 d.el.addClass(opt.cls);
11007             }
11008             d.proxyDrag = opt.proxyDrag === true;
11009             d.modal = opt.modal !== false;
11010             d.mask = opt.modal !== false ? mask : false;
11011             if(!d.isVisible()){
11012                 // force it to the end of the z-index stack so it gets a cursor in FF
11013                 document.body.appendChild(dlg.el.dom);
11014                 d.animateTarget = null;
11015                 d.show(options.animEl);
11016             }
11017             return this;
11018         },
11019
11020         /**
11021          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
11022          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
11023          * and closing the message box when the process is complete.
11024          * @param {String} title The title bar text
11025          * @param {String} msg The message box body text
11026          * @return {Roo.MessageBox} This message box
11027          */
11028         progress : function(title, msg){
11029             this.show({
11030                 title : title,
11031                 msg : msg,
11032                 buttons: false,
11033                 progress:true,
11034                 closable:false,
11035                 minWidth: this.minProgressWidth,
11036                 modal : true
11037             });
11038             return this;
11039         },
11040
11041         /**
11042          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
11043          * If a callback function is passed it will be called after the user clicks the button, and the
11044          * id of the button that was clicked will be passed as the only parameter to the callback
11045          * (could also be the top-right close button).
11046          * @param {String} title The title bar text
11047          * @param {String} msg The message box body text
11048          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11049          * @param {Object} scope (optional) The scope of the callback function
11050          * @return {Roo.MessageBox} This message box
11051          */
11052         alert : function(title, msg, fn, scope){
11053             this.show({
11054                 title : title,
11055                 msg : msg,
11056                 buttons: this.OK,
11057                 fn: fn,
11058                 scope : scope,
11059                 modal : true
11060             });
11061             return this;
11062         },
11063
11064         /**
11065          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
11066          * interaction while waiting for a long-running process to complete that does not have defined intervals.
11067          * You are responsible for closing the message box when the process is complete.
11068          * @param {String} msg The message box body text
11069          * @param {String} title (optional) The title bar text
11070          * @return {Roo.MessageBox} This message box
11071          */
11072         wait : function(msg, title){
11073             this.show({
11074                 title : title,
11075                 msg : msg,
11076                 buttons: false,
11077                 closable:false,
11078                 progress:true,
11079                 modal:true,
11080                 width:300,
11081                 wait:true
11082             });
11083             waitTimer = Roo.TaskMgr.start({
11084                 run: function(i){
11085                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
11086                 },
11087                 interval: 1000
11088             });
11089             return this;
11090         },
11091
11092         /**
11093          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
11094          * If a callback function is passed it will be called after the user clicks either button, and the id of the
11095          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
11096          * @param {String} title The title bar text
11097          * @param {String} msg The message box body text
11098          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11099          * @param {Object} scope (optional) The scope of the callback function
11100          * @return {Roo.MessageBox} This message box
11101          */
11102         confirm : function(title, msg, fn, scope){
11103             this.show({
11104                 title : title,
11105                 msg : msg,
11106                 buttons: this.YESNO,
11107                 fn: fn,
11108                 scope : scope,
11109                 modal : true
11110             });
11111             return this;
11112         },
11113
11114         /**
11115          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
11116          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
11117          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
11118          * (could also be the top-right close button) and the text that was entered will be passed as the two
11119          * parameters to the callback.
11120          * @param {String} title The title bar text
11121          * @param {String} msg The message box body text
11122          * @param {Function} fn (optional) The callback function invoked after the message box is closed
11123          * @param {Object} scope (optional) The scope of the callback function
11124          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
11125          * property, or the height in pixels to create the textbox (defaults to false / single-line)
11126          * @return {Roo.MessageBox} This message box
11127          */
11128         prompt : function(title, msg, fn, scope, multiline){
11129             this.show({
11130                 title : title,
11131                 msg : msg,
11132                 buttons: this.OKCANCEL,
11133                 fn: fn,
11134                 minWidth:250,
11135                 scope : scope,
11136                 prompt:true,
11137                 multiline: multiline,
11138                 modal : true
11139             });
11140             return this;
11141         },
11142
11143         /**
11144          * Button config that displays a single OK button
11145          * @type Object
11146          */
11147         OK : {ok:true},
11148         /**
11149          * Button config that displays Yes and No buttons
11150          * @type Object
11151          */
11152         YESNO : {yes:true, no:true},
11153         /**
11154          * Button config that displays OK and Cancel buttons
11155          * @type Object
11156          */
11157         OKCANCEL : {ok:true, cancel:true},
11158         /**
11159          * Button config that displays Yes, No and Cancel buttons
11160          * @type Object
11161          */
11162         YESNOCANCEL : {yes:true, no:true, cancel:true},
11163
11164         /**
11165          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
11166          * @type Number
11167          */
11168         defaultTextHeight : 75,
11169         /**
11170          * The maximum width in pixels of the message box (defaults to 600)
11171          * @type Number
11172          */
11173         maxWidth : 600,
11174         /**
11175          * The minimum width in pixels of the message box (defaults to 100)
11176          * @type Number
11177          */
11178         minWidth : 100,
11179         /**
11180          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
11181          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
11182          * @type Number
11183          */
11184         minProgressWidth : 250,
11185         /**
11186          * An object containing the default button text strings that can be overriden for localized language support.
11187          * Supported properties are: ok, cancel, yes and no.
11188          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
11189          * @type Object
11190          */
11191         buttonText : {
11192             ok : "OK",
11193             cancel : "Cancel",
11194             yes : "Yes",
11195             no : "No"
11196         }
11197     };
11198 }();
11199
11200 /**
11201  * Shorthand for {@link Roo.MessageBox}
11202  */
11203 Roo.Msg = Roo.MessageBox;/*
11204  * Based on:
11205  * Ext JS Library 1.1.1
11206  * Copyright(c) 2006-2007, Ext JS, LLC.
11207  *
11208  * Originally Released Under LGPL - original licence link has changed is not relivant.
11209  *
11210  * Fork - LGPL
11211  * <script type="text/javascript">
11212  */
11213 /**
11214  * @class Roo.QuickTips
11215  * Provides attractive and customizable tooltips for any element.
11216  * @singleton
11217  */
11218 Roo.QuickTips = function(){
11219     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
11220     var ce, bd, xy, dd;
11221     var visible = false, disabled = true, inited = false;
11222     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
11223     
11224     var onOver = function(e){
11225         if(disabled){
11226             return;
11227         }
11228         var t = e.getTarget();
11229         if(!t || t.nodeType !== 1 || t == document || t == document.body){
11230             return;
11231         }
11232         if(ce && t == ce.el){
11233             clearTimeout(hideProc);
11234             return;
11235         }
11236         if(t && tagEls[t.id]){
11237             tagEls[t.id].el = t;
11238             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
11239             return;
11240         }
11241         var ttp, et = Roo.fly(t);
11242         var ns = cfg.namespace;
11243         if(tm.interceptTitles && t.title){
11244             ttp = t.title;
11245             t.qtip = ttp;
11246             t.removeAttribute("title");
11247             e.preventDefault();
11248         }else{
11249             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
11250         }
11251         if(ttp){
11252             showProc = show.defer(tm.showDelay, tm, [{
11253                 el: t, 
11254                 text: ttp.replace(/\\n/g,'<br/>'),
11255                 width: et.getAttributeNS(ns, cfg.width),
11256                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
11257                 title: et.getAttributeNS(ns, cfg.title),
11258                     cls: et.getAttributeNS(ns, cfg.cls)
11259             }]);
11260         }
11261     };
11262     
11263     var onOut = function(e){
11264         clearTimeout(showProc);
11265         var t = e.getTarget();
11266         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
11267             hideProc = setTimeout(hide, tm.hideDelay);
11268         }
11269     };
11270     
11271     var onMove = function(e){
11272         if(disabled){
11273             return;
11274         }
11275         xy = e.getXY();
11276         xy[1] += 18;
11277         if(tm.trackMouse && ce){
11278             el.setXY(xy);
11279         }
11280     };
11281     
11282     var onDown = function(e){
11283         clearTimeout(showProc);
11284         clearTimeout(hideProc);
11285         if(!e.within(el)){
11286             if(tm.hideOnClick){
11287                 hide();
11288                 tm.disable();
11289                 tm.enable.defer(100, tm);
11290             }
11291         }
11292     };
11293     
11294     var getPad = function(){
11295         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
11296     };
11297
11298     var show = function(o){
11299         if(disabled){
11300             return;
11301         }
11302         clearTimeout(dismissProc);
11303         ce = o;
11304         if(removeCls){ // in case manually hidden
11305             el.removeClass(removeCls);
11306             removeCls = null;
11307         }
11308         if(ce.cls){
11309             el.addClass(ce.cls);
11310             removeCls = ce.cls;
11311         }
11312         if(ce.title){
11313             tipTitle.update(ce.title);
11314             tipTitle.show();
11315         }else{
11316             tipTitle.update('');
11317             tipTitle.hide();
11318         }
11319         el.dom.style.width  = tm.maxWidth+'px';
11320         //tipBody.dom.style.width = '';
11321         tipBodyText.update(o.text);
11322         var p = getPad(), w = ce.width;
11323         if(!w){
11324             var td = tipBodyText.dom;
11325             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
11326             if(aw > tm.maxWidth){
11327                 w = tm.maxWidth;
11328             }else if(aw < tm.minWidth){
11329                 w = tm.minWidth;
11330             }else{
11331                 w = aw;
11332             }
11333         }
11334         //tipBody.setWidth(w);
11335         el.setWidth(parseInt(w, 10) + p);
11336         if(ce.autoHide === false){
11337             close.setDisplayed(true);
11338             if(dd){
11339                 dd.unlock();
11340             }
11341         }else{
11342             close.setDisplayed(false);
11343             if(dd){
11344                 dd.lock();
11345             }
11346         }
11347         if(xy){
11348             el.avoidY = xy[1]-18;
11349             el.setXY(xy);
11350         }
11351         if(tm.animate){
11352             el.setOpacity(.1);
11353             el.setStyle("visibility", "visible");
11354             el.fadeIn({callback: afterShow});
11355         }else{
11356             afterShow();
11357         }
11358     };
11359     
11360     var afterShow = function(){
11361         if(ce){
11362             el.show();
11363             esc.enable();
11364             if(tm.autoDismiss && ce.autoHide !== false){
11365                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
11366             }
11367         }
11368     };
11369     
11370     var hide = function(noanim){
11371         clearTimeout(dismissProc);
11372         clearTimeout(hideProc);
11373         ce = null;
11374         if(el.isVisible()){
11375             esc.disable();
11376             if(noanim !== true && tm.animate){
11377                 el.fadeOut({callback: afterHide});
11378             }else{
11379                 afterHide();
11380             } 
11381         }
11382     };
11383     
11384     var afterHide = function(){
11385         el.hide();
11386         if(removeCls){
11387             el.removeClass(removeCls);
11388             removeCls = null;
11389         }
11390     };
11391     
11392     return {
11393         /**
11394         * @cfg {Number} minWidth
11395         * The minimum width of the quick tip (defaults to 40)
11396         */
11397        minWidth : 40,
11398         /**
11399         * @cfg {Number} maxWidth
11400         * The maximum width of the quick tip (defaults to 300)
11401         */
11402        maxWidth : 300,
11403         /**
11404         * @cfg {Boolean} interceptTitles
11405         * True to automatically use the element's DOM title value if available (defaults to false)
11406         */
11407        interceptTitles : false,
11408         /**
11409         * @cfg {Boolean} trackMouse
11410         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
11411         */
11412        trackMouse : false,
11413         /**
11414         * @cfg {Boolean} hideOnClick
11415         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
11416         */
11417        hideOnClick : true,
11418         /**
11419         * @cfg {Number} showDelay
11420         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
11421         */
11422        showDelay : 500,
11423         /**
11424         * @cfg {Number} hideDelay
11425         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
11426         */
11427        hideDelay : 200,
11428         /**
11429         * @cfg {Boolean} autoHide
11430         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11431         * Used in conjunction with hideDelay.
11432         */
11433        autoHide : true,
11434         /**
11435         * @cfg {Boolean}
11436         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11437         * (defaults to true).  Used in conjunction with autoDismissDelay.
11438         */
11439        autoDismiss : true,
11440         /**
11441         * @cfg {Number}
11442         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11443         */
11444        autoDismissDelay : 5000,
11445        /**
11446         * @cfg {Boolean} animate
11447         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11448         */
11449        animate : false,
11450
11451        /**
11452         * @cfg {String} title
11453         * Title text to display (defaults to '').  This can be any valid HTML markup.
11454         */
11455         title: '',
11456        /**
11457         * @cfg {String} text
11458         * Body text to display (defaults to '').  This can be any valid HTML markup.
11459         */
11460         text : '',
11461        /**
11462         * @cfg {String} cls
11463         * A CSS class to apply to the base quick tip element (defaults to '').
11464         */
11465         cls : '',
11466        /**
11467         * @cfg {Number} width
11468         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11469         * minWidth or maxWidth.
11470         */
11471         width : null,
11472
11473     /**
11474      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11475      * or display QuickTips in a page.
11476      */
11477        init : function(){
11478           tm = Roo.QuickTips;
11479           cfg = tm.tagConfig;
11480           if(!inited){
11481               if(!Roo.isReady){ // allow calling of init() before onReady
11482                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11483                   return;
11484               }
11485               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11486               el.fxDefaults = {stopFx: true};
11487               // maximum custom styling
11488               //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>');
11489               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>');              
11490               tipTitle = el.child('h3');
11491               tipTitle.enableDisplayMode("block");
11492               tipBody = el.child('div.x-tip-bd');
11493               tipBodyText = el.child('div.x-tip-bd-inner');
11494               //bdLeft = el.child('div.x-tip-bd-left');
11495               //bdRight = el.child('div.x-tip-bd-right');
11496               close = el.child('div.x-tip-close');
11497               close.enableDisplayMode("block");
11498               close.on("click", hide);
11499               var d = Roo.get(document);
11500               d.on("mousedown", onDown);
11501               d.on("mouseover", onOver);
11502               d.on("mouseout", onOut);
11503               d.on("mousemove", onMove);
11504               esc = d.addKeyListener(27, hide);
11505               esc.disable();
11506               if(Roo.dd.DD){
11507                   dd = el.initDD("default", null, {
11508                       onDrag : function(){
11509                           el.sync();  
11510                       }
11511                   });
11512                   dd.setHandleElId(tipTitle.id);
11513                   dd.lock();
11514               }
11515               inited = true;
11516           }
11517           this.enable(); 
11518        },
11519
11520     /**
11521      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11522      * are supported:
11523      * <pre>
11524 Property    Type                   Description
11525 ----------  ---------------------  ------------------------------------------------------------------------
11526 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11527      * </ul>
11528      * @param {Object} config The config object
11529      */
11530        register : function(config){
11531            var cs = config instanceof Array ? config : arguments;
11532            for(var i = 0, len = cs.length; i < len; i++) {
11533                var c = cs[i];
11534                var target = c.target;
11535                if(target){
11536                    if(target instanceof Array){
11537                        for(var j = 0, jlen = target.length; j < jlen; j++){
11538                            tagEls[target[j]] = c;
11539                        }
11540                    }else{
11541                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11542                    }
11543                }
11544            }
11545        },
11546
11547     /**
11548      * Removes this quick tip from its element and destroys it.
11549      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11550      */
11551        unregister : function(el){
11552            delete tagEls[Roo.id(el)];
11553        },
11554
11555     /**
11556      * Enable this quick tip.
11557      */
11558        enable : function(){
11559            if(inited && disabled){
11560                locks.pop();
11561                if(locks.length < 1){
11562                    disabled = false;
11563                }
11564            }
11565        },
11566
11567     /**
11568      * Disable this quick tip.
11569      */
11570        disable : function(){
11571           disabled = true;
11572           clearTimeout(showProc);
11573           clearTimeout(hideProc);
11574           clearTimeout(dismissProc);
11575           if(ce){
11576               hide(true);
11577           }
11578           locks.push(1);
11579        },
11580
11581     /**
11582      * Returns true if the quick tip is enabled, else false.
11583      */
11584        isEnabled : function(){
11585             return !disabled;
11586        },
11587
11588         // private
11589        tagConfig : {
11590            namespace : "roo", // was ext?? this may break..
11591            alt_namespace : "ext",
11592            attribute : "qtip",
11593            width : "width",
11594            target : "target",
11595            title : "qtitle",
11596            hide : "hide",
11597            cls : "qclass"
11598        }
11599    };
11600 }();
11601
11602 // backwards compat
11603 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11604  * Based on:
11605  * Ext JS Library 1.1.1
11606  * Copyright(c) 2006-2007, Ext JS, LLC.
11607  *
11608  * Originally Released Under LGPL - original licence link has changed is not relivant.
11609  *
11610  * Fork - LGPL
11611  * <script type="text/javascript">
11612  */
11613  
11614
11615 /**
11616  * @class Roo.tree.TreePanel
11617  * @extends Roo.data.Tree
11618
11619  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11620  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11621  * @cfg {Boolean} enableDD true to enable drag and drop
11622  * @cfg {Boolean} enableDrag true to enable just drag
11623  * @cfg {Boolean} enableDrop true to enable just drop
11624  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11625  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11626  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11627  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11628  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11629  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11630  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11631  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11632  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11633  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11634  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11635  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11636  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11637  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11638  * @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>
11639  * @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>
11640  * 
11641  * @constructor
11642  * @param {String/HTMLElement/Element} el The container element
11643  * @param {Object} config
11644  */
11645 Roo.tree.TreePanel = function(el, config){
11646     var root = false;
11647     var loader = false;
11648     if (config.root) {
11649         root = config.root;
11650         delete config.root;
11651     }
11652     if (config.loader) {
11653         loader = config.loader;
11654         delete config.loader;
11655     }
11656     
11657     Roo.apply(this, config);
11658     Roo.tree.TreePanel.superclass.constructor.call(this);
11659     this.el = Roo.get(el);
11660     this.el.addClass('x-tree');
11661     //console.log(root);
11662     if (root) {
11663         this.setRootNode( Roo.factory(root, Roo.tree));
11664     }
11665     if (loader) {
11666         this.loader = Roo.factory(loader, Roo.tree);
11667     }
11668    /**
11669     * Read-only. The id of the container element becomes this TreePanel's id.
11670     */
11671     this.id = this.el.id;
11672     this.addEvents({
11673         /**
11674         * @event beforeload
11675         * Fires before a node is loaded, return false to cancel
11676         * @param {Node} node The node being loaded
11677         */
11678         "beforeload" : true,
11679         /**
11680         * @event load
11681         * Fires when a node is loaded
11682         * @param {Node} node The node that was loaded
11683         */
11684         "load" : true,
11685         /**
11686         * @event textchange
11687         * Fires when the text for a node is changed
11688         * @param {Node} node The node
11689         * @param {String} text The new text
11690         * @param {String} oldText The old text
11691         */
11692         "textchange" : true,
11693         /**
11694         * @event beforeexpand
11695         * Fires before a node is expanded, return false to cancel.
11696         * @param {Node} node The node
11697         * @param {Boolean} deep
11698         * @param {Boolean} anim
11699         */
11700         "beforeexpand" : true,
11701         /**
11702         * @event beforecollapse
11703         * Fires before a node is collapsed, return false to cancel.
11704         * @param {Node} node The node
11705         * @param {Boolean} deep
11706         * @param {Boolean} anim
11707         */
11708         "beforecollapse" : true,
11709         /**
11710         * @event expand
11711         * Fires when a node is expanded
11712         * @param {Node} node The node
11713         */
11714         "expand" : true,
11715         /**
11716         * @event disabledchange
11717         * Fires when the disabled status of a node changes
11718         * @param {Node} node The node
11719         * @param {Boolean} disabled
11720         */
11721         "disabledchange" : true,
11722         /**
11723         * @event collapse
11724         * Fires when a node is collapsed
11725         * @param {Node} node The node
11726         */
11727         "collapse" : true,
11728         /**
11729         * @event beforeclick
11730         * Fires before click processing on a node. Return false to cancel the default action.
11731         * @param {Node} node The node
11732         * @param {Roo.EventObject} e The event object
11733         */
11734         "beforeclick":true,
11735         /**
11736         * @event checkchange
11737         * Fires when a node with a checkbox's checked property changes
11738         * @param {Node} this This node
11739         * @param {Boolean} checked
11740         */
11741         "checkchange":true,
11742         /**
11743         * @event click
11744         * Fires when a node is clicked
11745         * @param {Node} node The node
11746         * @param {Roo.EventObject} e The event object
11747         */
11748         "click":true,
11749         /**
11750         * @event dblclick
11751         * Fires when a node is double clicked
11752         * @param {Node} node The node
11753         * @param {Roo.EventObject} e The event object
11754         */
11755         "dblclick":true,
11756         /**
11757         * @event contextmenu
11758         * Fires when a node is right clicked
11759         * @param {Node} node The node
11760         * @param {Roo.EventObject} e The event object
11761         */
11762         "contextmenu":true,
11763         /**
11764         * @event beforechildrenrendered
11765         * Fires right before the child nodes for a node are rendered
11766         * @param {Node} node The node
11767         */
11768         "beforechildrenrendered":true,
11769         /**
11770         * @event startdrag
11771         * Fires when a node starts being dragged
11772         * @param {Roo.tree.TreePanel} this
11773         * @param {Roo.tree.TreeNode} node
11774         * @param {event} e The raw browser event
11775         */ 
11776        "startdrag" : true,
11777        /**
11778         * @event enddrag
11779         * Fires when a drag operation is complete
11780         * @param {Roo.tree.TreePanel} this
11781         * @param {Roo.tree.TreeNode} node
11782         * @param {event} e The raw browser event
11783         */
11784        "enddrag" : true,
11785        /**
11786         * @event dragdrop
11787         * Fires when a dragged node is dropped on a valid DD target
11788         * @param {Roo.tree.TreePanel} this
11789         * @param {Roo.tree.TreeNode} node
11790         * @param {DD} dd The dd it was dropped on
11791         * @param {event} e The raw browser event
11792         */
11793        "dragdrop" : true,
11794        /**
11795         * @event beforenodedrop
11796         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11797         * passed to handlers has the following properties:<br />
11798         * <ul style="padding:5px;padding-left:16px;">
11799         * <li>tree - The TreePanel</li>
11800         * <li>target - The node being targeted for the drop</li>
11801         * <li>data - The drag data from the drag source</li>
11802         * <li>point - The point of the drop - append, above or below</li>
11803         * <li>source - The drag source</li>
11804         * <li>rawEvent - Raw mouse event</li>
11805         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11806         * to be inserted by setting them on this object.</li>
11807         * <li>cancel - Set this to true to cancel the drop.</li>
11808         * </ul>
11809         * @param {Object} dropEvent
11810         */
11811        "beforenodedrop" : true,
11812        /**
11813         * @event nodedrop
11814         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11815         * passed to handlers has the following properties:<br />
11816         * <ul style="padding:5px;padding-left:16px;">
11817         * <li>tree - The TreePanel</li>
11818         * <li>target - The node being targeted for the drop</li>
11819         * <li>data - The drag data from the drag source</li>
11820         * <li>point - The point of the drop - append, above or below</li>
11821         * <li>source - The drag source</li>
11822         * <li>rawEvent - Raw mouse event</li>
11823         * <li>dropNode - Dropped node(s).</li>
11824         * </ul>
11825         * @param {Object} dropEvent
11826         */
11827        "nodedrop" : true,
11828         /**
11829         * @event nodedragover
11830         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11831         * passed to handlers has the following properties:<br />
11832         * <ul style="padding:5px;padding-left:16px;">
11833         * <li>tree - The TreePanel</li>
11834         * <li>target - The node being targeted for the drop</li>
11835         * <li>data - The drag data from the drag source</li>
11836         * <li>point - The point of the drop - append, above or below</li>
11837         * <li>source - The drag source</li>
11838         * <li>rawEvent - Raw mouse event</li>
11839         * <li>dropNode - Drop node(s) provided by the source.</li>
11840         * <li>cancel - Set this to true to signal drop not allowed.</li>
11841         * </ul>
11842         * @param {Object} dragOverEvent
11843         */
11844        "nodedragover" : true,
11845        /**
11846         * @event appendnode
11847         * Fires when append node to the tree
11848         * @param {Roo.tree.TreePanel} this
11849         * @param {Roo.tree.TreeNode} node
11850         * @param {Number} index The index of the newly appended node
11851         */
11852        "appendnode" : true
11853         
11854     });
11855     if(this.singleExpand){
11856        this.on("beforeexpand", this.restrictExpand, this);
11857     }
11858     if (this.editor) {
11859         this.editor.tree = this;
11860         this.editor = Roo.factory(this.editor, Roo.tree);
11861     }
11862     
11863     if (this.selModel) {
11864         this.selModel = Roo.factory(this.selModel, Roo.tree);
11865     }
11866    
11867 };
11868 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11869     rootVisible : true,
11870     animate: Roo.enableFx,
11871     lines : true,
11872     enableDD : false,
11873     hlDrop : Roo.enableFx,
11874   
11875     renderer: false,
11876     
11877     rendererTip: false,
11878     // private
11879     restrictExpand : function(node){
11880         var p = node.parentNode;
11881         if(p){
11882             if(p.expandedChild && p.expandedChild.parentNode == p){
11883                 p.expandedChild.collapse();
11884             }
11885             p.expandedChild = node;
11886         }
11887     },
11888
11889     // private override
11890     setRootNode : function(node){
11891         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11892         if(!this.rootVisible){
11893             node.ui = new Roo.tree.RootTreeNodeUI(node);
11894         }
11895         return node;
11896     },
11897
11898     /**
11899      * Returns the container element for this TreePanel
11900      */
11901     getEl : function(){
11902         return this.el;
11903     },
11904
11905     /**
11906      * Returns the default TreeLoader for this TreePanel
11907      */
11908     getLoader : function(){
11909         return this.loader;
11910     },
11911
11912     /**
11913      * Expand all nodes
11914      */
11915     expandAll : function(){
11916         this.root.expand(true);
11917     },
11918
11919     /**
11920      * Collapse all nodes
11921      */
11922     collapseAll : function(){
11923         this.root.collapse(true);
11924     },
11925
11926     /**
11927      * Returns the selection model used by this TreePanel
11928      */
11929     getSelectionModel : function(){
11930         if(!this.selModel){
11931             this.selModel = new Roo.tree.DefaultSelectionModel();
11932         }
11933         return this.selModel;
11934     },
11935
11936     /**
11937      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11938      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11939      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11940      * @return {Array}
11941      */
11942     getChecked : function(a, startNode){
11943         startNode = startNode || this.root;
11944         var r = [];
11945         var f = function(){
11946             if(this.attributes.checked){
11947                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11948             }
11949         }
11950         startNode.cascade(f);
11951         return r;
11952     },
11953
11954     /**
11955      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11956      * @param {String} path
11957      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11958      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11959      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11960      */
11961     expandPath : function(path, attr, callback){
11962         attr = attr || "id";
11963         var keys = path.split(this.pathSeparator);
11964         var curNode = this.root;
11965         if(curNode.attributes[attr] != keys[1]){ // invalid root
11966             if(callback){
11967                 callback(false, null);
11968             }
11969             return;
11970         }
11971         var index = 1;
11972         var f = function(){
11973             if(++index == keys.length){
11974                 if(callback){
11975                     callback(true, curNode);
11976                 }
11977                 return;
11978             }
11979             var c = curNode.findChild(attr, keys[index]);
11980             if(!c){
11981                 if(callback){
11982                     callback(false, curNode);
11983                 }
11984                 return;
11985             }
11986             curNode = c;
11987             c.expand(false, false, f);
11988         };
11989         curNode.expand(false, false, f);
11990     },
11991
11992     /**
11993      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11994      * @param {String} path
11995      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11996      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11997      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11998      */
11999     selectPath : function(path, attr, callback){
12000         attr = attr || "id";
12001         var keys = path.split(this.pathSeparator);
12002         var v = keys.pop();
12003         if(keys.length > 0){
12004             var f = function(success, node){
12005                 if(success && node){
12006                     var n = node.findChild(attr, v);
12007                     if(n){
12008                         n.select();
12009                         if(callback){
12010                             callback(true, n);
12011                         }
12012                     }else if(callback){
12013                         callback(false, n);
12014                     }
12015                 }else{
12016                     if(callback){
12017                         callback(false, n);
12018                     }
12019                 }
12020             };
12021             this.expandPath(keys.join(this.pathSeparator), attr, f);
12022         }else{
12023             this.root.select();
12024             if(callback){
12025                 callback(true, this.root);
12026             }
12027         }
12028     },
12029
12030     getTreeEl : function(){
12031         return this.el;
12032     },
12033
12034     /**
12035      * Trigger rendering of this TreePanel
12036      */
12037     render : function(){
12038         if (this.innerCt) {
12039             return this; // stop it rendering more than once!!
12040         }
12041         
12042         this.innerCt = this.el.createChild({tag:"ul",
12043                cls:"x-tree-root-ct " +
12044                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
12045
12046         if(this.containerScroll){
12047             Roo.dd.ScrollManager.register(this.el);
12048         }
12049         if((this.enableDD || this.enableDrop) && !this.dropZone){
12050            /**
12051             * The dropZone used by this tree if drop is enabled
12052             * @type Roo.tree.TreeDropZone
12053             */
12054              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
12055                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
12056            });
12057         }
12058         if((this.enableDD || this.enableDrag) && !this.dragZone){
12059            /**
12060             * The dragZone used by this tree if drag is enabled
12061             * @type Roo.tree.TreeDragZone
12062             */
12063             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
12064                ddGroup: this.ddGroup || "TreeDD",
12065                scroll: this.ddScroll
12066            });
12067         }
12068         this.getSelectionModel().init(this);
12069         if (!this.root) {
12070             Roo.log("ROOT not set in tree");
12071             return this;
12072         }
12073         this.root.render();
12074         if(!this.rootVisible){
12075             this.root.renderChildren();
12076         }
12077         return this;
12078     }
12079 });/*
12080  * Based on:
12081  * Ext JS Library 1.1.1
12082  * Copyright(c) 2006-2007, Ext JS, LLC.
12083  *
12084  * Originally Released Under LGPL - original licence link has changed is not relivant.
12085  *
12086  * Fork - LGPL
12087  * <script type="text/javascript">
12088  */
12089  
12090
12091 /**
12092  * @class Roo.tree.DefaultSelectionModel
12093  * @extends Roo.util.Observable
12094  * The default single selection for a TreePanel.
12095  * @param {Object} cfg Configuration
12096  */
12097 Roo.tree.DefaultSelectionModel = function(cfg){
12098    this.selNode = null;
12099    
12100    
12101    
12102    this.addEvents({
12103        /**
12104         * @event selectionchange
12105         * Fires when the selected node changes
12106         * @param {DefaultSelectionModel} this
12107         * @param {TreeNode} node the new selection
12108         */
12109        "selectionchange" : true,
12110
12111        /**
12112         * @event beforeselect
12113         * Fires before the selected node changes, return false to cancel the change
12114         * @param {DefaultSelectionModel} this
12115         * @param {TreeNode} node the new selection
12116         * @param {TreeNode} node the old selection
12117         */
12118        "beforeselect" : true
12119    });
12120    
12121     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
12122 };
12123
12124 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
12125     init : function(tree){
12126         this.tree = tree;
12127         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12128         tree.on("click", this.onNodeClick, this);
12129     },
12130     
12131     onNodeClick : function(node, e){
12132         if (e.ctrlKey && this.selNode == node)  {
12133             this.unselect(node);
12134             return;
12135         }
12136         this.select(node);
12137     },
12138     
12139     /**
12140      * Select a node.
12141      * @param {TreeNode} node The node to select
12142      * @return {TreeNode} The selected node
12143      */
12144     select : function(node){
12145         var last = this.selNode;
12146         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
12147             if(last){
12148                 last.ui.onSelectedChange(false);
12149             }
12150             this.selNode = node;
12151             node.ui.onSelectedChange(true);
12152             this.fireEvent("selectionchange", this, node, last);
12153         }
12154         return node;
12155     },
12156     
12157     /**
12158      * Deselect a node.
12159      * @param {TreeNode} node The node to unselect
12160      */
12161     unselect : function(node){
12162         if(this.selNode == node){
12163             this.clearSelections();
12164         }    
12165     },
12166     
12167     /**
12168      * Clear all selections
12169      */
12170     clearSelections : function(){
12171         var n = this.selNode;
12172         if(n){
12173             n.ui.onSelectedChange(false);
12174             this.selNode = null;
12175             this.fireEvent("selectionchange", this, null);
12176         }
12177         return n;
12178     },
12179     
12180     /**
12181      * Get the selected node
12182      * @return {TreeNode} The selected node
12183      */
12184     getSelectedNode : function(){
12185         return this.selNode;    
12186     },
12187     
12188     /**
12189      * Returns true if the node is selected
12190      * @param {TreeNode} node The node to check
12191      * @return {Boolean}
12192      */
12193     isSelected : function(node){
12194         return this.selNode == node;  
12195     },
12196
12197     /**
12198      * Selects the node above the selected node in the tree, intelligently walking the nodes
12199      * @return TreeNode The new selection
12200      */
12201     selectPrevious : function(){
12202         var s = this.selNode || this.lastSelNode;
12203         if(!s){
12204             return null;
12205         }
12206         var ps = s.previousSibling;
12207         if(ps){
12208             if(!ps.isExpanded() || ps.childNodes.length < 1){
12209                 return this.select(ps);
12210             } else{
12211                 var lc = ps.lastChild;
12212                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
12213                     lc = lc.lastChild;
12214                 }
12215                 return this.select(lc);
12216             }
12217         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
12218             return this.select(s.parentNode);
12219         }
12220         return null;
12221     },
12222
12223     /**
12224      * Selects the node above the selected node in the tree, intelligently walking the nodes
12225      * @return TreeNode The new selection
12226      */
12227     selectNext : function(){
12228         var s = this.selNode || this.lastSelNode;
12229         if(!s){
12230             return null;
12231         }
12232         if(s.firstChild && s.isExpanded()){
12233              return this.select(s.firstChild);
12234          }else if(s.nextSibling){
12235              return this.select(s.nextSibling);
12236          }else if(s.parentNode){
12237             var newS = null;
12238             s.parentNode.bubble(function(){
12239                 if(this.nextSibling){
12240                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
12241                     return false;
12242                 }
12243             });
12244             return newS;
12245          }
12246         return null;
12247     },
12248
12249     onKeyDown : function(e){
12250         var s = this.selNode || this.lastSelNode;
12251         // undesirable, but required
12252         var sm = this;
12253         if(!s){
12254             return;
12255         }
12256         var k = e.getKey();
12257         switch(k){
12258              case e.DOWN:
12259                  e.stopEvent();
12260                  this.selectNext();
12261              break;
12262              case e.UP:
12263                  e.stopEvent();
12264                  this.selectPrevious();
12265              break;
12266              case e.RIGHT:
12267                  e.preventDefault();
12268                  if(s.hasChildNodes()){
12269                      if(!s.isExpanded()){
12270                          s.expand();
12271                      }else if(s.firstChild){
12272                          this.select(s.firstChild, e);
12273                      }
12274                  }
12275              break;
12276              case e.LEFT:
12277                  e.preventDefault();
12278                  if(s.hasChildNodes() && s.isExpanded()){
12279                      s.collapse();
12280                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
12281                      this.select(s.parentNode, e);
12282                  }
12283              break;
12284         };
12285     }
12286 });
12287
12288 /**
12289  * @class Roo.tree.MultiSelectionModel
12290  * @extends Roo.util.Observable
12291  * Multi selection for a TreePanel.
12292  * @param {Object} cfg Configuration
12293  */
12294 Roo.tree.MultiSelectionModel = function(){
12295    this.selNodes = [];
12296    this.selMap = {};
12297    this.addEvents({
12298        /**
12299         * @event selectionchange
12300         * Fires when the selected nodes change
12301         * @param {MultiSelectionModel} this
12302         * @param {Array} nodes Array of the selected nodes
12303         */
12304        "selectionchange" : true
12305    });
12306    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
12307    
12308 };
12309
12310 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
12311     init : function(tree){
12312         this.tree = tree;
12313         tree.getTreeEl().on("keydown", this.onKeyDown, this);
12314         tree.on("click", this.onNodeClick, this);
12315     },
12316     
12317     onNodeClick : function(node, e){
12318         this.select(node, e, e.ctrlKey);
12319     },
12320     
12321     /**
12322      * Select a node.
12323      * @param {TreeNode} node The node to select
12324      * @param {EventObject} e (optional) An event associated with the selection
12325      * @param {Boolean} keepExisting True to retain existing selections
12326      * @return {TreeNode} The selected node
12327      */
12328     select : function(node, e, keepExisting){
12329         if(keepExisting !== true){
12330             this.clearSelections(true);
12331         }
12332         if(this.isSelected(node)){
12333             this.lastSelNode = node;
12334             return node;
12335         }
12336         this.selNodes.push(node);
12337         this.selMap[node.id] = node;
12338         this.lastSelNode = node;
12339         node.ui.onSelectedChange(true);
12340         this.fireEvent("selectionchange", this, this.selNodes);
12341         return node;
12342     },
12343     
12344     /**
12345      * Deselect a node.
12346      * @param {TreeNode} node The node to unselect
12347      */
12348     unselect : function(node){
12349         if(this.selMap[node.id]){
12350             node.ui.onSelectedChange(false);
12351             var sn = this.selNodes;
12352             var index = -1;
12353             if(sn.indexOf){
12354                 index = sn.indexOf(node);
12355             }else{
12356                 for(var i = 0, len = sn.length; i < len; i++){
12357                     if(sn[i] == node){
12358                         index = i;
12359                         break;
12360                     }
12361                 }
12362             }
12363             if(index != -1){
12364                 this.selNodes.splice(index, 1);
12365             }
12366             delete this.selMap[node.id];
12367             this.fireEvent("selectionchange", this, this.selNodes);
12368         }
12369     },
12370     
12371     /**
12372      * Clear all selections
12373      */
12374     clearSelections : function(suppressEvent){
12375         var sn = this.selNodes;
12376         if(sn.length > 0){
12377             for(var i = 0, len = sn.length; i < len; i++){
12378                 sn[i].ui.onSelectedChange(false);
12379             }
12380             this.selNodes = [];
12381             this.selMap = {};
12382             if(suppressEvent !== true){
12383                 this.fireEvent("selectionchange", this, this.selNodes);
12384             }
12385         }
12386     },
12387     
12388     /**
12389      * Returns true if the node is selected
12390      * @param {TreeNode} node The node to check
12391      * @return {Boolean}
12392      */
12393     isSelected : function(node){
12394         return this.selMap[node.id] ? true : false;  
12395     },
12396     
12397     /**
12398      * Returns an array of the selected nodes
12399      * @return {Array}
12400      */
12401     getSelectedNodes : function(){
12402         return this.selNodes;    
12403     },
12404
12405     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
12406
12407     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
12408
12409     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
12410 });/*
12411  * Based on:
12412  * Ext JS Library 1.1.1
12413  * Copyright(c) 2006-2007, Ext JS, LLC.
12414  *
12415  * Originally Released Under LGPL - original licence link has changed is not relivant.
12416  *
12417  * Fork - LGPL
12418  * <script type="text/javascript">
12419  */
12420  
12421 /**
12422  * @class Roo.tree.TreeNode
12423  * @extends Roo.data.Node
12424  * @cfg {String} text The text for this node
12425  * @cfg {Boolean} expanded true to start the node expanded
12426  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
12427  * @cfg {Boolean} allowDrop false if this node cannot be drop on
12428  * @cfg {Boolean} disabled true to start the node disabled
12429  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
12430  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12431  * @cfg {String} cls A css class to be added to the node
12432  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12433  * @cfg {String} href URL of the link used for the node (defaults to #)
12434  * @cfg {String} hrefTarget target frame for the link
12435  * @cfg {String} qtip An Ext QuickTip for the node
12436  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12437  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12438  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12439  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12440  * (defaults to undefined with no checkbox rendered)
12441  * @constructor
12442  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12443  */
12444 Roo.tree.TreeNode = function(attributes){
12445     attributes = attributes || {};
12446     if(typeof attributes == "string"){
12447         attributes = {text: attributes};
12448     }
12449     this.childrenRendered = false;
12450     this.rendered = false;
12451     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12452     this.expanded = attributes.expanded === true;
12453     this.isTarget = attributes.isTarget !== false;
12454     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12455     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12456
12457     /**
12458      * Read-only. The text for this node. To change it use setText().
12459      * @type String
12460      */
12461     this.text = attributes.text;
12462     /**
12463      * True if this node is disabled.
12464      * @type Boolean
12465      */
12466     this.disabled = attributes.disabled === true;
12467
12468     this.addEvents({
12469         /**
12470         * @event textchange
12471         * Fires when the text for this node is changed
12472         * @param {Node} this This node
12473         * @param {String} text The new text
12474         * @param {String} oldText The old text
12475         */
12476         "textchange" : true,
12477         /**
12478         * @event beforeexpand
12479         * Fires before this node is expanded, return false to cancel.
12480         * @param {Node} this This node
12481         * @param {Boolean} deep
12482         * @param {Boolean} anim
12483         */
12484         "beforeexpand" : true,
12485         /**
12486         * @event beforecollapse
12487         * Fires before this node is collapsed, return false to cancel.
12488         * @param {Node} this This node
12489         * @param {Boolean} deep
12490         * @param {Boolean} anim
12491         */
12492         "beforecollapse" : true,
12493         /**
12494         * @event expand
12495         * Fires when this node is expanded
12496         * @param {Node} this This node
12497         */
12498         "expand" : true,
12499         /**
12500         * @event disabledchange
12501         * Fires when the disabled status of this node changes
12502         * @param {Node} this This node
12503         * @param {Boolean} disabled
12504         */
12505         "disabledchange" : true,
12506         /**
12507         * @event collapse
12508         * Fires when this node is collapsed
12509         * @param {Node} this This node
12510         */
12511         "collapse" : true,
12512         /**
12513         * @event beforeclick
12514         * Fires before click processing. Return false to cancel the default action.
12515         * @param {Node} this This node
12516         * @param {Roo.EventObject} e The event object
12517         */
12518         "beforeclick":true,
12519         /**
12520         * @event checkchange
12521         * Fires when a node with a checkbox's checked property changes
12522         * @param {Node} this This node
12523         * @param {Boolean} checked
12524         */
12525         "checkchange":true,
12526         /**
12527         * @event click
12528         * Fires when this node is clicked
12529         * @param {Node} this This node
12530         * @param {Roo.EventObject} e The event object
12531         */
12532         "click":true,
12533         /**
12534         * @event dblclick
12535         * Fires when this node is double clicked
12536         * @param {Node} this This node
12537         * @param {Roo.EventObject} e The event object
12538         */
12539         "dblclick":true,
12540         /**
12541         * @event contextmenu
12542         * Fires when this node is right clicked
12543         * @param {Node} this This node
12544         * @param {Roo.EventObject} e The event object
12545         */
12546         "contextmenu":true,
12547         /**
12548         * @event beforechildrenrendered
12549         * Fires right before the child nodes for this node are rendered
12550         * @param {Node} this This node
12551         */
12552         "beforechildrenrendered":true
12553     });
12554
12555     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12556
12557     /**
12558      * Read-only. The UI for this node
12559      * @type TreeNodeUI
12560      */
12561     this.ui = new uiClass(this);
12562     
12563     // finally support items[]
12564     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12565         return;
12566     }
12567     
12568     
12569     Roo.each(this.attributes.items, function(c) {
12570         this.appendChild(Roo.factory(c,Roo.Tree));
12571     }, this);
12572     delete this.attributes.items;
12573     
12574     
12575     
12576 };
12577 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12578     preventHScroll: true,
12579     /**
12580      * Returns true if this node is expanded
12581      * @return {Boolean}
12582      */
12583     isExpanded : function(){
12584         return this.expanded;
12585     },
12586
12587     /**
12588      * Returns the UI object for this node
12589      * @return {TreeNodeUI}
12590      */
12591     getUI : function(){
12592         return this.ui;
12593     },
12594
12595     // private override
12596     setFirstChild : function(node){
12597         var of = this.firstChild;
12598         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12599         if(this.childrenRendered && of && node != of){
12600             of.renderIndent(true, true);
12601         }
12602         if(this.rendered){
12603             this.renderIndent(true, true);
12604         }
12605     },
12606
12607     // private override
12608     setLastChild : function(node){
12609         var ol = this.lastChild;
12610         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12611         if(this.childrenRendered && ol && node != ol){
12612             ol.renderIndent(true, true);
12613         }
12614         if(this.rendered){
12615             this.renderIndent(true, true);
12616         }
12617     },
12618
12619     // these methods are overridden to provide lazy rendering support
12620     // private override
12621     appendChild : function()
12622     {
12623         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12624         if(node && this.childrenRendered){
12625             node.render();
12626         }
12627         this.ui.updateExpandIcon();
12628         return node;
12629     },
12630
12631     // private override
12632     removeChild : function(node){
12633         this.ownerTree.getSelectionModel().unselect(node);
12634         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12635         // if it's been rendered remove dom node
12636         if(this.childrenRendered){
12637             node.ui.remove();
12638         }
12639         if(this.childNodes.length < 1){
12640             this.collapse(false, false);
12641         }else{
12642             this.ui.updateExpandIcon();
12643         }
12644         if(!this.firstChild) {
12645             this.childrenRendered = false;
12646         }
12647         return node;
12648     },
12649
12650     // private override
12651     insertBefore : function(node, refNode){
12652         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12653         if(newNode && refNode && this.childrenRendered){
12654             node.render();
12655         }
12656         this.ui.updateExpandIcon();
12657         return newNode;
12658     },
12659
12660     /**
12661      * Sets the text for this node
12662      * @param {String} text
12663      */
12664     setText : function(text){
12665         var oldText = this.text;
12666         this.text = text;
12667         this.attributes.text = text;
12668         if(this.rendered){ // event without subscribing
12669             this.ui.onTextChange(this, text, oldText);
12670         }
12671         this.fireEvent("textchange", this, text, oldText);
12672     },
12673
12674     /**
12675      * Triggers selection of this node
12676      */
12677     select : function(){
12678         this.getOwnerTree().getSelectionModel().select(this);
12679     },
12680
12681     /**
12682      * Triggers deselection of this node
12683      */
12684     unselect : function(){
12685         this.getOwnerTree().getSelectionModel().unselect(this);
12686     },
12687
12688     /**
12689      * Returns true if this node is selected
12690      * @return {Boolean}
12691      */
12692     isSelected : function(){
12693         return this.getOwnerTree().getSelectionModel().isSelected(this);
12694     },
12695
12696     /**
12697      * Expand this node.
12698      * @param {Boolean} deep (optional) True to expand all children as well
12699      * @param {Boolean} anim (optional) false to cancel the default animation
12700      * @param {Function} callback (optional) A callback to be called when
12701      * expanding this node completes (does not wait for deep expand to complete).
12702      * Called with 1 parameter, this node.
12703      */
12704     expand : function(deep, anim, callback){
12705         if(!this.expanded){
12706             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12707                 return;
12708             }
12709             if(!this.childrenRendered){
12710                 this.renderChildren();
12711             }
12712             this.expanded = true;
12713             
12714             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12715                 this.ui.animExpand(function(){
12716                     this.fireEvent("expand", this);
12717                     if(typeof callback == "function"){
12718                         callback(this);
12719                     }
12720                     if(deep === true){
12721                         this.expandChildNodes(true);
12722                     }
12723                 }.createDelegate(this));
12724                 return;
12725             }else{
12726                 this.ui.expand();
12727                 this.fireEvent("expand", this);
12728                 if(typeof callback == "function"){
12729                     callback(this);
12730                 }
12731             }
12732         }else{
12733            if(typeof callback == "function"){
12734                callback(this);
12735            }
12736         }
12737         if(deep === true){
12738             this.expandChildNodes(true);
12739         }
12740     },
12741
12742     isHiddenRoot : function(){
12743         return this.isRoot && !this.getOwnerTree().rootVisible;
12744     },
12745
12746     /**
12747      * Collapse this node.
12748      * @param {Boolean} deep (optional) True to collapse all children as well
12749      * @param {Boolean} anim (optional) false to cancel the default animation
12750      */
12751     collapse : function(deep, anim){
12752         if(this.expanded && !this.isHiddenRoot()){
12753             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12754                 return;
12755             }
12756             this.expanded = false;
12757             if((this.getOwnerTree().animate && anim !== false) || anim){
12758                 this.ui.animCollapse(function(){
12759                     this.fireEvent("collapse", this);
12760                     if(deep === true){
12761                         this.collapseChildNodes(true);
12762                     }
12763                 }.createDelegate(this));
12764                 return;
12765             }else{
12766                 this.ui.collapse();
12767                 this.fireEvent("collapse", this);
12768             }
12769         }
12770         if(deep === true){
12771             var cs = this.childNodes;
12772             for(var i = 0, len = cs.length; i < len; i++) {
12773                 cs[i].collapse(true, false);
12774             }
12775         }
12776     },
12777
12778     // private
12779     delayedExpand : function(delay){
12780         if(!this.expandProcId){
12781             this.expandProcId = this.expand.defer(delay, this);
12782         }
12783     },
12784
12785     // private
12786     cancelExpand : function(){
12787         if(this.expandProcId){
12788             clearTimeout(this.expandProcId);
12789         }
12790         this.expandProcId = false;
12791     },
12792
12793     /**
12794      * Toggles expanded/collapsed state of the node
12795      */
12796     toggle : function(){
12797         if(this.expanded){
12798             this.collapse();
12799         }else{
12800             this.expand();
12801         }
12802     },
12803
12804     /**
12805      * Ensures all parent nodes are expanded
12806      */
12807     ensureVisible : function(callback){
12808         var tree = this.getOwnerTree();
12809         tree.expandPath(this.parentNode.getPath(), false, function(){
12810             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12811             Roo.callback(callback);
12812         }.createDelegate(this));
12813     },
12814
12815     /**
12816      * Expand all child nodes
12817      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12818      */
12819     expandChildNodes : function(deep){
12820         var cs = this.childNodes;
12821         for(var i = 0, len = cs.length; i < len; i++) {
12822                 cs[i].expand(deep);
12823         }
12824     },
12825
12826     /**
12827      * Collapse all child nodes
12828      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12829      */
12830     collapseChildNodes : function(deep){
12831         var cs = this.childNodes;
12832         for(var i = 0, len = cs.length; i < len; i++) {
12833                 cs[i].collapse(deep);
12834         }
12835     },
12836
12837     /**
12838      * Disables this node
12839      */
12840     disable : function(){
12841         this.disabled = true;
12842         this.unselect();
12843         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12844             this.ui.onDisableChange(this, true);
12845         }
12846         this.fireEvent("disabledchange", this, true);
12847     },
12848
12849     /**
12850      * Enables this node
12851      */
12852     enable : function(){
12853         this.disabled = false;
12854         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12855             this.ui.onDisableChange(this, false);
12856         }
12857         this.fireEvent("disabledchange", this, false);
12858     },
12859
12860     // private
12861     renderChildren : function(suppressEvent){
12862         if(suppressEvent !== false){
12863             this.fireEvent("beforechildrenrendered", this);
12864         }
12865         var cs = this.childNodes;
12866         for(var i = 0, len = cs.length; i < len; i++){
12867             cs[i].render(true);
12868         }
12869         this.childrenRendered = true;
12870     },
12871
12872     // private
12873     sort : function(fn, scope){
12874         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12875         if(this.childrenRendered){
12876             var cs = this.childNodes;
12877             for(var i = 0, len = cs.length; i < len; i++){
12878                 cs[i].render(true);
12879             }
12880         }
12881     },
12882
12883     // private
12884     render : function(bulkRender){
12885         this.ui.render(bulkRender);
12886         if(!this.rendered){
12887             this.rendered = true;
12888             if(this.expanded){
12889                 this.expanded = false;
12890                 this.expand(false, false);
12891             }
12892         }
12893     },
12894
12895     // private
12896     renderIndent : function(deep, refresh){
12897         if(refresh){
12898             this.ui.childIndent = null;
12899         }
12900         this.ui.renderIndent();
12901         if(deep === true && this.childrenRendered){
12902             var cs = this.childNodes;
12903             for(var i = 0, len = cs.length; i < len; i++){
12904                 cs[i].renderIndent(true, refresh);
12905             }
12906         }
12907     }
12908 });/*
12909  * Based on:
12910  * Ext JS Library 1.1.1
12911  * Copyright(c) 2006-2007, Ext JS, LLC.
12912  *
12913  * Originally Released Under LGPL - original licence link has changed is not relivant.
12914  *
12915  * Fork - LGPL
12916  * <script type="text/javascript">
12917  */
12918  
12919 /**
12920  * @class Roo.tree.AsyncTreeNode
12921  * @extends Roo.tree.TreeNode
12922  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12923  * @constructor
12924  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12925  */
12926  Roo.tree.AsyncTreeNode = function(config){
12927     this.loaded = false;
12928     this.loading = false;
12929     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12930     /**
12931     * @event beforeload
12932     * Fires before this node is loaded, return false to cancel
12933     * @param {Node} this This node
12934     */
12935     this.addEvents({'beforeload':true, 'load': true});
12936     /**
12937     * @event load
12938     * Fires when this node is loaded
12939     * @param {Node} this This node
12940     */
12941     /**
12942      * The loader used by this node (defaults to using the tree's defined loader)
12943      * @type TreeLoader
12944      * @property loader
12945      */
12946 };
12947 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12948     expand : function(deep, anim, callback){
12949         if(this.loading){ // if an async load is already running, waiting til it's done
12950             var timer;
12951             var f = function(){
12952                 if(!this.loading){ // done loading
12953                     clearInterval(timer);
12954                     this.expand(deep, anim, callback);
12955                 }
12956             }.createDelegate(this);
12957             timer = setInterval(f, 200);
12958             return;
12959         }
12960         if(!this.loaded){
12961             if(this.fireEvent("beforeload", this) === false){
12962                 return;
12963             }
12964             this.loading = true;
12965             this.ui.beforeLoad(this);
12966             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12967             if(loader){
12968                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12969                 return;
12970             }
12971         }
12972         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12973     },
12974     
12975     /**
12976      * Returns true if this node is currently loading
12977      * @return {Boolean}
12978      */
12979     isLoading : function(){
12980         return this.loading;  
12981     },
12982     
12983     loadComplete : function(deep, anim, callback){
12984         this.loading = false;
12985         this.loaded = true;
12986         this.ui.afterLoad(this);
12987         this.fireEvent("load", this);
12988         this.expand(deep, anim, callback);
12989     },
12990     
12991     /**
12992      * Returns true if this node has been loaded
12993      * @return {Boolean}
12994      */
12995     isLoaded : function(){
12996         return this.loaded;
12997     },
12998     
12999     hasChildNodes : function(){
13000         if(!this.isLeaf() && !this.loaded){
13001             return true;
13002         }else{
13003             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
13004         }
13005     },
13006
13007     /**
13008      * Trigger a reload for this node
13009      * @param {Function} callback
13010      */
13011     reload : function(callback){
13012         this.collapse(false, false);
13013         while(this.firstChild){
13014             this.removeChild(this.firstChild);
13015         }
13016         this.childrenRendered = false;
13017         this.loaded = false;
13018         if(this.isHiddenRoot()){
13019             this.expanded = false;
13020         }
13021         this.expand(false, false, callback);
13022     }
13023 });/*
13024  * Based on:
13025  * Ext JS Library 1.1.1
13026  * Copyright(c) 2006-2007, Ext JS, LLC.
13027  *
13028  * Originally Released Under LGPL - original licence link has changed is not relivant.
13029  *
13030  * Fork - LGPL
13031  * <script type="text/javascript">
13032  */
13033  
13034 /**
13035  * @class Roo.tree.TreeNodeUI
13036  * @constructor
13037  * @param {Object} node The node to render
13038  * The TreeNode UI implementation is separate from the
13039  * tree implementation. Unless you are customizing the tree UI,
13040  * you should never have to use this directly.
13041  */
13042 Roo.tree.TreeNodeUI = function(node){
13043     this.node = node;
13044     this.rendered = false;
13045     this.animating = false;
13046     this.emptyIcon = Roo.BLANK_IMAGE_URL;
13047 };
13048
13049 Roo.tree.TreeNodeUI.prototype = {
13050     removeChild : function(node){
13051         if(this.rendered){
13052             this.ctNode.removeChild(node.ui.getEl());
13053         }
13054     },
13055
13056     beforeLoad : function(){
13057          this.addClass("x-tree-node-loading");
13058     },
13059
13060     afterLoad : function(){
13061          this.removeClass("x-tree-node-loading");
13062     },
13063
13064     onTextChange : function(node, text, oldText){
13065         if(this.rendered){
13066             this.textNode.innerHTML = text;
13067         }
13068     },
13069
13070     onDisableChange : function(node, state){
13071         this.disabled = state;
13072         if(state){
13073             this.addClass("x-tree-node-disabled");
13074         }else{
13075             this.removeClass("x-tree-node-disabled");
13076         }
13077     },
13078
13079     onSelectedChange : function(state){
13080         if(state){
13081             this.focus();
13082             this.addClass("x-tree-selected");
13083         }else{
13084             //this.blur();
13085             this.removeClass("x-tree-selected");
13086         }
13087     },
13088
13089     onMove : function(tree, node, oldParent, newParent, index, refNode){
13090         this.childIndent = null;
13091         if(this.rendered){
13092             var targetNode = newParent.ui.getContainer();
13093             if(!targetNode){//target not rendered
13094                 this.holder = document.createElement("div");
13095                 this.holder.appendChild(this.wrap);
13096                 return;
13097             }
13098             var insertBefore = refNode ? refNode.ui.getEl() : null;
13099             if(insertBefore){
13100                 targetNode.insertBefore(this.wrap, insertBefore);
13101             }else{
13102                 targetNode.appendChild(this.wrap);
13103             }
13104             this.node.renderIndent(true);
13105         }
13106     },
13107
13108     addClass : function(cls){
13109         if(this.elNode){
13110             Roo.fly(this.elNode).addClass(cls);
13111         }
13112     },
13113
13114     removeClass : function(cls){
13115         if(this.elNode){
13116             Roo.fly(this.elNode).removeClass(cls);
13117         }
13118     },
13119
13120     remove : function(){
13121         if(this.rendered){
13122             this.holder = document.createElement("div");
13123             this.holder.appendChild(this.wrap);
13124         }
13125     },
13126
13127     fireEvent : function(){
13128         return this.node.fireEvent.apply(this.node, arguments);
13129     },
13130
13131     initEvents : function(){
13132         this.node.on("move", this.onMove, this);
13133         var E = Roo.EventManager;
13134         var a = this.anchor;
13135
13136         var el = Roo.fly(a, '_treeui');
13137
13138         if(Roo.isOpera){ // opera render bug ignores the CSS
13139             el.setStyle("text-decoration", "none");
13140         }
13141
13142         el.on("click", this.onClick, this);
13143         el.on("dblclick", this.onDblClick, this);
13144
13145         if(this.checkbox){
13146             Roo.EventManager.on(this.checkbox,
13147                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
13148         }
13149
13150         el.on("contextmenu", this.onContextMenu, this);
13151
13152         var icon = Roo.fly(this.iconNode);
13153         icon.on("click", this.onClick, this);
13154         icon.on("dblclick", this.onDblClick, this);
13155         icon.on("contextmenu", this.onContextMenu, this);
13156         E.on(this.ecNode, "click", this.ecClick, this, true);
13157
13158         if(this.node.disabled){
13159             this.addClass("x-tree-node-disabled");
13160         }
13161         if(this.node.hidden){
13162             this.addClass("x-tree-node-disabled");
13163         }
13164         var ot = this.node.getOwnerTree();
13165         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
13166         if(dd && (!this.node.isRoot || ot.rootVisible)){
13167             Roo.dd.Registry.register(this.elNode, {
13168                 node: this.node,
13169                 handles: this.getDDHandles(),
13170                 isHandle: false
13171             });
13172         }
13173     },
13174
13175     getDDHandles : function(){
13176         return [this.iconNode, this.textNode];
13177     },
13178
13179     hide : function(){
13180         if(this.rendered){
13181             this.wrap.style.display = "none";
13182         }
13183     },
13184
13185     show : function(){
13186         if(this.rendered){
13187             this.wrap.style.display = "";
13188         }
13189     },
13190
13191     onContextMenu : function(e){
13192         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
13193             e.preventDefault();
13194             this.focus();
13195             this.fireEvent("contextmenu", this.node, e);
13196         }
13197     },
13198
13199     onClick : function(e){
13200         if(this.dropping){
13201             e.stopEvent();
13202             return;
13203         }
13204         if(this.fireEvent("beforeclick", this.node, e) !== false){
13205             if(!this.disabled && this.node.attributes.href){
13206                 this.fireEvent("click", this.node, e);
13207                 return;
13208             }
13209             e.preventDefault();
13210             if(this.disabled){
13211                 return;
13212             }
13213
13214             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
13215                 this.node.toggle();
13216             }
13217
13218             this.fireEvent("click", this.node, e);
13219         }else{
13220             e.stopEvent();
13221         }
13222     },
13223
13224     onDblClick : function(e){
13225         e.preventDefault();
13226         if(this.disabled){
13227             return;
13228         }
13229         if(this.checkbox){
13230             this.toggleCheck();
13231         }
13232         if(!this.animating && this.node.hasChildNodes()){
13233             this.node.toggle();
13234         }
13235         this.fireEvent("dblclick", this.node, e);
13236     },
13237
13238     onCheckChange : function(){
13239         var checked = this.checkbox.checked;
13240         this.node.attributes.checked = checked;
13241         this.fireEvent('checkchange', this.node, checked);
13242     },
13243
13244     ecClick : function(e){
13245         if(!this.animating && this.node.hasChildNodes()){
13246             this.node.toggle();
13247         }
13248     },
13249
13250     startDrop : function(){
13251         this.dropping = true;
13252     },
13253
13254     // delayed drop so the click event doesn't get fired on a drop
13255     endDrop : function(){
13256        setTimeout(function(){
13257            this.dropping = false;
13258        }.createDelegate(this), 50);
13259     },
13260
13261     expand : function(){
13262         this.updateExpandIcon();
13263         this.ctNode.style.display = "";
13264     },
13265
13266     focus : function(){
13267         if(!this.node.preventHScroll){
13268             try{this.anchor.focus();
13269             }catch(e){}
13270         }else if(!Roo.isIE){
13271             try{
13272                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
13273                 var l = noscroll.scrollLeft;
13274                 this.anchor.focus();
13275                 noscroll.scrollLeft = l;
13276             }catch(e){}
13277         }
13278     },
13279
13280     toggleCheck : function(value){
13281         var cb = this.checkbox;
13282         if(cb){
13283             cb.checked = (value === undefined ? !cb.checked : value);
13284         }
13285     },
13286
13287     blur : function(){
13288         try{
13289             this.anchor.blur();
13290         }catch(e){}
13291     },
13292
13293     animExpand : function(callback){
13294         var ct = Roo.get(this.ctNode);
13295         ct.stopFx();
13296         if(!this.node.hasChildNodes()){
13297             this.updateExpandIcon();
13298             this.ctNode.style.display = "";
13299             Roo.callback(callback);
13300             return;
13301         }
13302         this.animating = true;
13303         this.updateExpandIcon();
13304
13305         ct.slideIn('t', {
13306            callback : function(){
13307                this.animating = false;
13308                Roo.callback(callback);
13309             },
13310             scope: this,
13311             duration: this.node.ownerTree.duration || .25
13312         });
13313     },
13314
13315     highlight : function(){
13316         var tree = this.node.getOwnerTree();
13317         Roo.fly(this.wrap).highlight(
13318             tree.hlColor || "C3DAF9",
13319             {endColor: tree.hlBaseColor}
13320         );
13321     },
13322
13323     collapse : function(){
13324         this.updateExpandIcon();
13325         this.ctNode.style.display = "none";
13326     },
13327
13328     animCollapse : function(callback){
13329         var ct = Roo.get(this.ctNode);
13330         ct.enableDisplayMode('block');
13331         ct.stopFx();
13332
13333         this.animating = true;
13334         this.updateExpandIcon();
13335
13336         ct.slideOut('t', {
13337             callback : function(){
13338                this.animating = false;
13339                Roo.callback(callback);
13340             },
13341             scope: this,
13342             duration: this.node.ownerTree.duration || .25
13343         });
13344     },
13345
13346     getContainer : function(){
13347         return this.ctNode;
13348     },
13349
13350     getEl : function(){
13351         return this.wrap;
13352     },
13353
13354     appendDDGhost : function(ghostNode){
13355         ghostNode.appendChild(this.elNode.cloneNode(true));
13356     },
13357
13358     getDDRepairXY : function(){
13359         return Roo.lib.Dom.getXY(this.iconNode);
13360     },
13361
13362     onRender : function(){
13363         this.render();
13364     },
13365
13366     render : function(bulkRender){
13367         var n = this.node, a = n.attributes;
13368         var targetNode = n.parentNode ?
13369               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
13370
13371         if(!this.rendered){
13372             this.rendered = true;
13373
13374             this.renderElements(n, a, targetNode, bulkRender);
13375
13376             if(a.qtip){
13377                if(this.textNode.setAttributeNS){
13378                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
13379                    if(a.qtipTitle){
13380                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
13381                    }
13382                }else{
13383                    this.textNode.setAttribute("ext:qtip", a.qtip);
13384                    if(a.qtipTitle){
13385                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
13386                    }
13387                }
13388             }else if(a.qtipCfg){
13389                 a.qtipCfg.target = Roo.id(this.textNode);
13390                 Roo.QuickTips.register(a.qtipCfg);
13391             }
13392             this.initEvents();
13393             if(!this.node.expanded){
13394                 this.updateExpandIcon();
13395             }
13396         }else{
13397             if(bulkRender === true) {
13398                 targetNode.appendChild(this.wrap);
13399             }
13400         }
13401     },
13402
13403     renderElements : function(n, a, targetNode, bulkRender)
13404     {
13405         // add some indent caching, this helps performance when rendering a large tree
13406         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
13407         var t = n.getOwnerTree();
13408         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
13409         if (typeof(n.attributes.html) != 'undefined') {
13410             txt = n.attributes.html;
13411         }
13412         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
13413         var cb = typeof a.checked == 'boolean';
13414         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
13415         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
13416             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
13417             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
13418             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
13419             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
13420             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
13421              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
13422                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
13423             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
13424             "</li>"];
13425
13426         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
13427             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
13428                                 n.nextSibling.ui.getEl(), buf.join(""));
13429         }else{
13430             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13431         }
13432
13433         this.elNode = this.wrap.childNodes[0];
13434         this.ctNode = this.wrap.childNodes[1];
13435         var cs = this.elNode.childNodes;
13436         this.indentNode = cs[0];
13437         this.ecNode = cs[1];
13438         this.iconNode = cs[2];
13439         var index = 3;
13440         if(cb){
13441             this.checkbox = cs[3];
13442             index++;
13443         }
13444         this.anchor = cs[index];
13445         this.textNode = cs[index].firstChild;
13446     },
13447
13448     getAnchor : function(){
13449         return this.anchor;
13450     },
13451
13452     getTextEl : function(){
13453         return this.textNode;
13454     },
13455
13456     getIconEl : function(){
13457         return this.iconNode;
13458     },
13459
13460     isChecked : function(){
13461         return this.checkbox ? this.checkbox.checked : false;
13462     },
13463
13464     updateExpandIcon : function(){
13465         if(this.rendered){
13466             var n = this.node, c1, c2;
13467             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13468             var hasChild = n.hasChildNodes();
13469             if(hasChild){
13470                 if(n.expanded){
13471                     cls += "-minus";
13472                     c1 = "x-tree-node-collapsed";
13473                     c2 = "x-tree-node-expanded";
13474                 }else{
13475                     cls += "-plus";
13476                     c1 = "x-tree-node-expanded";
13477                     c2 = "x-tree-node-collapsed";
13478                 }
13479                 if(this.wasLeaf){
13480                     this.removeClass("x-tree-node-leaf");
13481                     this.wasLeaf = false;
13482                 }
13483                 if(this.c1 != c1 || this.c2 != c2){
13484                     Roo.fly(this.elNode).replaceClass(c1, c2);
13485                     this.c1 = c1; this.c2 = c2;
13486                 }
13487             }else{
13488                 // this changes non-leafs into leafs if they have no children.
13489                 // it's not very rational behaviour..
13490                 
13491                 if(!this.wasLeaf && this.node.leaf){
13492                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13493                     delete this.c1;
13494                     delete this.c2;
13495                     this.wasLeaf = true;
13496                 }
13497             }
13498             var ecc = "x-tree-ec-icon "+cls;
13499             if(this.ecc != ecc){
13500                 this.ecNode.className = ecc;
13501                 this.ecc = ecc;
13502             }
13503         }
13504     },
13505
13506     getChildIndent : function(){
13507         if(!this.childIndent){
13508             var buf = [];
13509             var p = this.node;
13510             while(p){
13511                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13512                     if(!p.isLast()) {
13513                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13514                     } else {
13515                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13516                     }
13517                 }
13518                 p = p.parentNode;
13519             }
13520             this.childIndent = buf.join("");
13521         }
13522         return this.childIndent;
13523     },
13524
13525     renderIndent : function(){
13526         if(this.rendered){
13527             var indent = "";
13528             var p = this.node.parentNode;
13529             if(p){
13530                 indent = p.ui.getChildIndent();
13531             }
13532             if(this.indentMarkup != indent){ // don't rerender if not required
13533                 this.indentNode.innerHTML = indent;
13534                 this.indentMarkup = indent;
13535             }
13536             this.updateExpandIcon();
13537         }
13538     }
13539 };
13540
13541 Roo.tree.RootTreeNodeUI = function(){
13542     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13543 };
13544 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13545     render : function(){
13546         if(!this.rendered){
13547             var targetNode = this.node.ownerTree.innerCt.dom;
13548             this.node.expanded = true;
13549             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13550             this.wrap = this.ctNode = targetNode.firstChild;
13551         }
13552     },
13553     collapse : function(){
13554     },
13555     expand : function(){
13556     }
13557 });/*
13558  * Based on:
13559  * Ext JS Library 1.1.1
13560  * Copyright(c) 2006-2007, Ext JS, LLC.
13561  *
13562  * Originally Released Under LGPL - original licence link has changed is not relivant.
13563  *
13564  * Fork - LGPL
13565  * <script type="text/javascript">
13566  */
13567 /**
13568  * @class Roo.tree.TreeLoader
13569  * @extends Roo.util.Observable
13570  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13571  * nodes from a specified URL. The response must be a javascript Array definition
13572  * who's elements are node definition objects. eg:
13573  * <pre><code>
13574 {  success : true,
13575    data :      [
13576    
13577     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13578     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13579     ]
13580 }
13581
13582
13583 </code></pre>
13584  * <br><br>
13585  * The old style respose with just an array is still supported, but not recommended.
13586  * <br><br>
13587  *
13588  * A server request is sent, and child nodes are loaded only when a node is expanded.
13589  * The loading node's id is passed to the server under the parameter name "node" to
13590  * enable the server to produce the correct child nodes.
13591  * <br><br>
13592  * To pass extra parameters, an event handler may be attached to the "beforeload"
13593  * event, and the parameters specified in the TreeLoader's baseParams property:
13594  * <pre><code>
13595     myTreeLoader.on("beforeload", function(treeLoader, node) {
13596         this.baseParams.category = node.attributes.category;
13597     }, this);
13598     
13599 </code></pre>
13600  *
13601  * This would pass an HTTP parameter called "category" to the server containing
13602  * the value of the Node's "category" attribute.
13603  * @constructor
13604  * Creates a new Treeloader.
13605  * @param {Object} config A config object containing config properties.
13606  */
13607 Roo.tree.TreeLoader = function(config){
13608     this.baseParams = {};
13609     this.requestMethod = "POST";
13610     Roo.apply(this, config);
13611
13612     this.addEvents({
13613     
13614         /**
13615          * @event beforeload
13616          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13617          * @param {Object} This TreeLoader object.
13618          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13619          * @param {Object} callback The callback function specified in the {@link #load} call.
13620          */
13621         beforeload : true,
13622         /**
13623          * @event load
13624          * Fires when the node has been successfuly loaded.
13625          * @param {Object} This TreeLoader object.
13626          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13627          * @param {Object} response The response object containing the data from the server.
13628          */
13629         load : true,
13630         /**
13631          * @event loadexception
13632          * Fires if the network request failed.
13633          * @param {Object} This TreeLoader object.
13634          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13635          * @param {Object} response The response object containing the data from the server.
13636          */
13637         loadexception : true,
13638         /**
13639          * @event create
13640          * Fires before a node is created, enabling you to return custom Node types 
13641          * @param {Object} This TreeLoader object.
13642          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13643          */
13644         create : true
13645     });
13646
13647     Roo.tree.TreeLoader.superclass.constructor.call(this);
13648 };
13649
13650 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13651     /**
13652     * @cfg {String} dataUrl The URL from which to request a Json string which
13653     * specifies an array of node definition object representing the child nodes
13654     * to be loaded.
13655     */
13656     /**
13657     * @cfg {String} requestMethod either GET or POST
13658     * defaults to POST (due to BC)
13659     * to be loaded.
13660     */
13661     /**
13662     * @cfg {Object} baseParams (optional) An object containing properties which
13663     * specify HTTP parameters to be passed to each request for child nodes.
13664     */
13665     /**
13666     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13667     * created by this loader. If the attributes sent by the server have an attribute in this object,
13668     * they take priority.
13669     */
13670     /**
13671     * @cfg {Object} uiProviders (optional) An object containing properties which
13672     * 
13673     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13674     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13675     * <i>uiProvider</i> attribute of a returned child node is a string rather
13676     * than a reference to a TreeNodeUI implementation, this that string value
13677     * is used as a property name in the uiProviders object. You can define the provider named
13678     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13679     */
13680     uiProviders : {},
13681
13682     /**
13683     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13684     * child nodes before loading.
13685     */
13686     clearOnLoad : true,
13687
13688     /**
13689     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13690     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13691     * Grid query { data : [ .....] }
13692     */
13693     
13694     root : false,
13695      /**
13696     * @cfg {String} queryParam (optional) 
13697     * Name of the query as it will be passed on the querystring (defaults to 'node')
13698     * eg. the request will be ?node=[id]
13699     */
13700     
13701     
13702     queryParam: false,
13703     
13704     /**
13705      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13706      * This is called automatically when a node is expanded, but may be used to reload
13707      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13708      * @param {Roo.tree.TreeNode} node
13709      * @param {Function} callback
13710      */
13711     load : function(node, callback){
13712         if(this.clearOnLoad){
13713             while(node.firstChild){
13714                 node.removeChild(node.firstChild);
13715             }
13716         }
13717         if(node.attributes.children){ // preloaded json children
13718             var cs = node.attributes.children;
13719             for(var i = 0, len = cs.length; i < len; i++){
13720                 node.appendChild(this.createNode(cs[i]));
13721             }
13722             if(typeof callback == "function"){
13723                 callback();
13724             }
13725         }else if(this.dataUrl){
13726             this.requestData(node, callback);
13727         }
13728     },
13729
13730     getParams: function(node){
13731         var buf = [], bp = this.baseParams;
13732         for(var key in bp){
13733             if(typeof bp[key] != "function"){
13734                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13735             }
13736         }
13737         var n = this.queryParam === false ? 'node' : this.queryParam;
13738         buf.push(n + "=", encodeURIComponent(node.id));
13739         return buf.join("");
13740     },
13741
13742     requestData : function(node, callback){
13743         if(this.fireEvent("beforeload", this, node, callback) !== false){
13744             this.transId = Roo.Ajax.request({
13745                 method:this.requestMethod,
13746                 url: this.dataUrl||this.url,
13747                 success: this.handleResponse,
13748                 failure: this.handleFailure,
13749                 scope: this,
13750                 argument: {callback: callback, node: node},
13751                 params: this.getParams(node)
13752             });
13753         }else{
13754             // if the load is cancelled, make sure we notify
13755             // the node that we are done
13756             if(typeof callback == "function"){
13757                 callback();
13758             }
13759         }
13760     },
13761
13762     isLoading : function(){
13763         return this.transId ? true : false;
13764     },
13765
13766     abort : function(){
13767         if(this.isLoading()){
13768             Roo.Ajax.abort(this.transId);
13769         }
13770     },
13771
13772     // private
13773     createNode : function(attr)
13774     {
13775         // apply baseAttrs, nice idea Corey!
13776         if(this.baseAttrs){
13777             Roo.applyIf(attr, this.baseAttrs);
13778         }
13779         if(this.applyLoader !== false){
13780             attr.loader = this;
13781         }
13782         // uiProvider = depreciated..
13783         
13784         if(typeof(attr.uiProvider) == 'string'){
13785            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13786                 /**  eval:var:attr */ eval(attr.uiProvider);
13787         }
13788         if(typeof(this.uiProviders['default']) != 'undefined') {
13789             attr.uiProvider = this.uiProviders['default'];
13790         }
13791         
13792         this.fireEvent('create', this, attr);
13793         
13794         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13795         return(attr.leaf ?
13796                         new Roo.tree.TreeNode(attr) :
13797                         new Roo.tree.AsyncTreeNode(attr));
13798     },
13799
13800     processResponse : function(response, node, callback)
13801     {
13802         var json = response.responseText;
13803         try {
13804             
13805             var o = Roo.decode(json);
13806             
13807             if (this.root === false && typeof(o.success) != undefined) {
13808                 this.root = 'data'; // the default behaviour for list like data..
13809                 }
13810                 
13811             if (this.root !== false &&  !o.success) {
13812                 // it's a failure condition.
13813                 var a = response.argument;
13814                 this.fireEvent("loadexception", this, a.node, response);
13815                 Roo.log("Load failed - should have a handler really");
13816                 return;
13817             }
13818             
13819             
13820             
13821             if (this.root !== false) {
13822                  o = o[this.root];
13823             }
13824             
13825             for(var i = 0, len = o.length; i < len; i++){
13826                 var n = this.createNode(o[i]);
13827                 if(n){
13828                     node.appendChild(n);
13829                 }
13830             }
13831             if(typeof callback == "function"){
13832                 callback(this, node);
13833             }
13834         }catch(e){
13835             this.handleFailure(response);
13836         }
13837     },
13838
13839     handleResponse : function(response){
13840         this.transId = false;
13841         var a = response.argument;
13842         this.processResponse(response, a.node, a.callback);
13843         this.fireEvent("load", this, a.node, response);
13844     },
13845
13846     handleFailure : function(response)
13847     {
13848         // should handle failure better..
13849         this.transId = false;
13850         var a = response.argument;
13851         this.fireEvent("loadexception", this, a.node, response);
13852         if(typeof a.callback == "function"){
13853             a.callback(this, a.node);
13854         }
13855     }
13856 });/*
13857  * Based on:
13858  * Ext JS Library 1.1.1
13859  * Copyright(c) 2006-2007, Ext JS, LLC.
13860  *
13861  * Originally Released Under LGPL - original licence link has changed is not relivant.
13862  *
13863  * Fork - LGPL
13864  * <script type="text/javascript">
13865  */
13866
13867 /**
13868 * @class Roo.tree.TreeFilter
13869 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13870 * @param {TreePanel} tree
13871 * @param {Object} config (optional)
13872  */
13873 Roo.tree.TreeFilter = function(tree, config){
13874     this.tree = tree;
13875     this.filtered = {};
13876     Roo.apply(this, config);
13877 };
13878
13879 Roo.tree.TreeFilter.prototype = {
13880     clearBlank:false,
13881     reverse:false,
13882     autoClear:false,
13883     remove:false,
13884
13885      /**
13886      * Filter the data by a specific attribute.
13887      * @param {String/RegExp} value Either string that the attribute value
13888      * should start with or a RegExp to test against the attribute
13889      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13890      * @param {TreeNode} startNode (optional) The node to start the filter at.
13891      */
13892     filter : function(value, attr, startNode){
13893         attr = attr || "text";
13894         var f;
13895         if(typeof value == "string"){
13896             var vlen = value.length;
13897             // auto clear empty filter
13898             if(vlen == 0 && this.clearBlank){
13899                 this.clear();
13900                 return;
13901             }
13902             value = value.toLowerCase();
13903             f = function(n){
13904                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13905             };
13906         }else if(value.exec){ // regex?
13907             f = function(n){
13908                 return value.test(n.attributes[attr]);
13909             };
13910         }else{
13911             throw 'Illegal filter type, must be string or regex';
13912         }
13913         this.filterBy(f, null, startNode);
13914         },
13915
13916     /**
13917      * Filter by a function. The passed function will be called with each
13918      * node in the tree (or from the startNode). If the function returns true, the node is kept
13919      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13920      * @param {Function} fn The filter function
13921      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13922      */
13923     filterBy : function(fn, scope, startNode){
13924         startNode = startNode || this.tree.root;
13925         if(this.autoClear){
13926             this.clear();
13927         }
13928         var af = this.filtered, rv = this.reverse;
13929         var f = function(n){
13930             if(n == startNode){
13931                 return true;
13932             }
13933             if(af[n.id]){
13934                 return false;
13935             }
13936             var m = fn.call(scope || n, n);
13937             if(!m || rv){
13938                 af[n.id] = n;
13939                 n.ui.hide();
13940                 return false;
13941             }
13942             return true;
13943         };
13944         startNode.cascade(f);
13945         if(this.remove){
13946            for(var id in af){
13947                if(typeof id != "function"){
13948                    var n = af[id];
13949                    if(n && n.parentNode){
13950                        n.parentNode.removeChild(n);
13951                    }
13952                }
13953            }
13954         }
13955     },
13956
13957     /**
13958      * Clears the current filter. Note: with the "remove" option
13959      * set a filter cannot be cleared.
13960      */
13961     clear : function(){
13962         var t = this.tree;
13963         var af = this.filtered;
13964         for(var id in af){
13965             if(typeof id != "function"){
13966                 var n = af[id];
13967                 if(n){
13968                     n.ui.show();
13969                 }
13970             }
13971         }
13972         this.filtered = {};
13973     }
13974 };
13975 /*
13976  * Based on:
13977  * Ext JS Library 1.1.1
13978  * Copyright(c) 2006-2007, Ext JS, LLC.
13979  *
13980  * Originally Released Under LGPL - original licence link has changed is not relivant.
13981  *
13982  * Fork - LGPL
13983  * <script type="text/javascript">
13984  */
13985  
13986
13987 /**
13988  * @class Roo.tree.TreeSorter
13989  * Provides sorting of nodes in a TreePanel
13990  * 
13991  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13992  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13993  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13994  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13995  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13996  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13997  * @constructor
13998  * @param {TreePanel} tree
13999  * @param {Object} config
14000  */
14001 Roo.tree.TreeSorter = function(tree, config){
14002     Roo.apply(this, config);
14003     tree.on("beforechildrenrendered", this.doSort, this);
14004     tree.on("append", this.updateSort, this);
14005     tree.on("insert", this.updateSort, this);
14006     
14007     var dsc = this.dir && this.dir.toLowerCase() == "desc";
14008     var p = this.property || "text";
14009     var sortType = this.sortType;
14010     var fs = this.folderSort;
14011     var cs = this.caseSensitive === true;
14012     var leafAttr = this.leafAttr || 'leaf';
14013
14014     this.sortFn = function(n1, n2){
14015         if(fs){
14016             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
14017                 return 1;
14018             }
14019             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
14020                 return -1;
14021             }
14022         }
14023         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
14024         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
14025         if(v1 < v2){
14026                         return dsc ? +1 : -1;
14027                 }else if(v1 > v2){
14028                         return dsc ? -1 : +1;
14029         }else{
14030                 return 0;
14031         }
14032     };
14033 };
14034
14035 Roo.tree.TreeSorter.prototype = {
14036     doSort : function(node){
14037         node.sort(this.sortFn);
14038     },
14039     
14040     compareNodes : function(n1, n2){
14041         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
14042     },
14043     
14044     updateSort : function(tree, node){
14045         if(node.childrenRendered){
14046             this.doSort.defer(1, this, [node]);
14047         }
14048     }
14049 };/*
14050  * Based on:
14051  * Ext JS Library 1.1.1
14052  * Copyright(c) 2006-2007, Ext JS, LLC.
14053  *
14054  * Originally Released Under LGPL - original licence link has changed is not relivant.
14055  *
14056  * Fork - LGPL
14057  * <script type="text/javascript">
14058  */
14059
14060 if(Roo.dd.DropZone){
14061     
14062 Roo.tree.TreeDropZone = function(tree, config){
14063     this.allowParentInsert = false;
14064     this.allowContainerDrop = false;
14065     this.appendOnly = false;
14066     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
14067     this.tree = tree;
14068     this.lastInsertClass = "x-tree-no-status";
14069     this.dragOverData = {};
14070 };
14071
14072 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
14073     ddGroup : "TreeDD",
14074     scroll:  true,
14075     
14076     expandDelay : 1000,
14077     
14078     expandNode : function(node){
14079         if(node.hasChildNodes() && !node.isExpanded()){
14080             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
14081         }
14082     },
14083     
14084     queueExpand : function(node){
14085         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
14086     },
14087     
14088     cancelExpand : function(){
14089         if(this.expandProcId){
14090             clearTimeout(this.expandProcId);
14091             this.expandProcId = false;
14092         }
14093     },
14094     
14095     isValidDropPoint : function(n, pt, dd, e, data){
14096         if(!n || !data){ return false; }
14097         var targetNode = n.node;
14098         var dropNode = data.node;
14099         // default drop rules
14100         if(!(targetNode && targetNode.isTarget && pt)){
14101             return false;
14102         }
14103         if(pt == "append" && targetNode.allowChildren === false){
14104             return false;
14105         }
14106         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
14107             return false;
14108         }
14109         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
14110             return false;
14111         }
14112         // reuse the object
14113         var overEvent = this.dragOverData;
14114         overEvent.tree = this.tree;
14115         overEvent.target = targetNode;
14116         overEvent.data = data;
14117         overEvent.point = pt;
14118         overEvent.source = dd;
14119         overEvent.rawEvent = e;
14120         overEvent.dropNode = dropNode;
14121         overEvent.cancel = false;  
14122         var result = this.tree.fireEvent("nodedragover", overEvent);
14123         return overEvent.cancel === false && result !== false;
14124     },
14125     
14126     getDropPoint : function(e, n, dd)
14127     {
14128         var tn = n.node;
14129         if(tn.isRoot){
14130             return tn.allowChildren !== false ? "append" : false; // always append for root
14131         }
14132         var dragEl = n.ddel;
14133         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
14134         var y = Roo.lib.Event.getPageY(e);
14135         //var noAppend = tn.allowChildren === false || tn.isLeaf();
14136         
14137         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
14138         var noAppend = tn.allowChildren === false;
14139         if(this.appendOnly || tn.parentNode.allowChildren === false){
14140             return noAppend ? false : "append";
14141         }
14142         var noBelow = false;
14143         if(!this.allowParentInsert){
14144             noBelow = tn.hasChildNodes() && tn.isExpanded();
14145         }
14146         var q = (b - t) / (noAppend ? 2 : 3);
14147         if(y >= t && y < (t + q)){
14148             return "above";
14149         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
14150             return "below";
14151         }else{
14152             return "append";
14153         }
14154     },
14155     
14156     onNodeEnter : function(n, dd, e, data)
14157     {
14158         this.cancelExpand();
14159     },
14160     
14161     onNodeOver : function(n, dd, e, data)
14162     {
14163        
14164         var pt = this.getDropPoint(e, n, dd);
14165         var node = n.node;
14166         
14167         // auto node expand check
14168         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
14169             this.queueExpand(node);
14170         }else if(pt != "append"){
14171             this.cancelExpand();
14172         }
14173         
14174         // set the insert point style on the target node
14175         var returnCls = this.dropNotAllowed;
14176         if(this.isValidDropPoint(n, pt, dd, e, data)){
14177            if(pt){
14178                var el = n.ddel;
14179                var cls;
14180                if(pt == "above"){
14181                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
14182                    cls = "x-tree-drag-insert-above";
14183                }else if(pt == "below"){
14184                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
14185                    cls = "x-tree-drag-insert-below";
14186                }else{
14187                    returnCls = "x-tree-drop-ok-append";
14188                    cls = "x-tree-drag-append";
14189                }
14190                if(this.lastInsertClass != cls){
14191                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
14192                    this.lastInsertClass = cls;
14193                }
14194            }
14195        }
14196        return returnCls;
14197     },
14198     
14199     onNodeOut : function(n, dd, e, data){
14200         
14201         this.cancelExpand();
14202         this.removeDropIndicators(n);
14203     },
14204     
14205     onNodeDrop : function(n, dd, e, data){
14206         var point = this.getDropPoint(e, n, dd);
14207         var targetNode = n.node;
14208         targetNode.ui.startDrop();
14209         if(!this.isValidDropPoint(n, point, dd, e, data)){
14210             targetNode.ui.endDrop();
14211             return false;
14212         }
14213         // first try to find the drop node
14214         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
14215         var dropEvent = {
14216             tree : this.tree,
14217             target: targetNode,
14218             data: data,
14219             point: point,
14220             source: dd,
14221             rawEvent: e,
14222             dropNode: dropNode,
14223             cancel: !dropNode   
14224         };
14225         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
14226         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
14227             targetNode.ui.endDrop();
14228             return false;
14229         }
14230         // allow target changing
14231         targetNode = dropEvent.target;
14232         if(point == "append" && !targetNode.isExpanded()){
14233             targetNode.expand(false, null, function(){
14234                 this.completeDrop(dropEvent);
14235             }.createDelegate(this));
14236         }else{
14237             this.completeDrop(dropEvent);
14238         }
14239         return true;
14240     },
14241     
14242     completeDrop : function(de){
14243         var ns = de.dropNode, p = de.point, t = de.target;
14244         if(!(ns instanceof Array)){
14245             ns = [ns];
14246         }
14247         var n;
14248         for(var i = 0, len = ns.length; i < len; i++){
14249             n = ns[i];
14250             if(p == "above"){
14251                 t.parentNode.insertBefore(n, t);
14252             }else if(p == "below"){
14253                 t.parentNode.insertBefore(n, t.nextSibling);
14254             }else{
14255                 t.appendChild(n);
14256             }
14257         }
14258         n.ui.focus();
14259         if(this.tree.hlDrop){
14260             n.ui.highlight();
14261         }
14262         t.ui.endDrop();
14263         this.tree.fireEvent("nodedrop", de);
14264     },
14265     
14266     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
14267         if(this.tree.hlDrop){
14268             dropNode.ui.focus();
14269             dropNode.ui.highlight();
14270         }
14271         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
14272     },
14273     
14274     getTree : function(){
14275         return this.tree;
14276     },
14277     
14278     removeDropIndicators : function(n){
14279         if(n && n.ddel){
14280             var el = n.ddel;
14281             Roo.fly(el).removeClass([
14282                     "x-tree-drag-insert-above",
14283                     "x-tree-drag-insert-below",
14284                     "x-tree-drag-append"]);
14285             this.lastInsertClass = "_noclass";
14286         }
14287     },
14288     
14289     beforeDragDrop : function(target, e, id){
14290         this.cancelExpand();
14291         return true;
14292     },
14293     
14294     afterRepair : function(data){
14295         if(data && Roo.enableFx){
14296             data.node.ui.highlight();
14297         }
14298         this.hideProxy();
14299     } 
14300     
14301 });
14302
14303 }
14304 /*
14305  * Based on:
14306  * Ext JS Library 1.1.1
14307  * Copyright(c) 2006-2007, Ext JS, LLC.
14308  *
14309  * Originally Released Under LGPL - original licence link has changed is not relivant.
14310  *
14311  * Fork - LGPL
14312  * <script type="text/javascript">
14313  */
14314  
14315
14316 if(Roo.dd.DragZone){
14317 Roo.tree.TreeDragZone = function(tree, config){
14318     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
14319     this.tree = tree;
14320 };
14321
14322 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
14323     ddGroup : "TreeDD",
14324    
14325     onBeforeDrag : function(data, e){
14326         var n = data.node;
14327         return n && n.draggable && !n.disabled;
14328     },
14329      
14330     
14331     onInitDrag : function(e){
14332         var data = this.dragData;
14333         this.tree.getSelectionModel().select(data.node);
14334         this.proxy.update("");
14335         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
14336         this.tree.fireEvent("startdrag", this.tree, data.node, e);
14337     },
14338     
14339     getRepairXY : function(e, data){
14340         return data.node.ui.getDDRepairXY();
14341     },
14342     
14343     onEndDrag : function(data, e){
14344         this.tree.fireEvent("enddrag", this.tree, data.node, e);
14345         
14346         
14347     },
14348     
14349     onValidDrop : function(dd, e, id){
14350         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
14351         this.hideProxy();
14352     },
14353     
14354     beforeInvalidDrop : function(e, id){
14355         // this scrolls the original position back into view
14356         var sm = this.tree.getSelectionModel();
14357         sm.clearSelections();
14358         sm.select(this.dragData.node);
14359     }
14360 });
14361 }/*
14362  * Based on:
14363  * Ext JS Library 1.1.1
14364  * Copyright(c) 2006-2007, Ext JS, LLC.
14365  *
14366  * Originally Released Under LGPL - original licence link has changed is not relivant.
14367  *
14368  * Fork - LGPL
14369  * <script type="text/javascript">
14370  */
14371 /**
14372  * @class Roo.tree.TreeEditor
14373  * @extends Roo.Editor
14374  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
14375  * as the editor field.
14376  * @constructor
14377  * @param {Object} config (used to be the tree panel.)
14378  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
14379  * 
14380  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
14381  * @cfg {Roo.form.TextField|Object} field The field configuration
14382  *
14383  * 
14384  */
14385 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
14386     var tree = config;
14387     var field;
14388     if (oldconfig) { // old style..
14389         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
14390     } else {
14391         // new style..
14392         tree = config.tree;
14393         config.field = config.field  || {};
14394         config.field.xtype = 'TextField';
14395         field = Roo.factory(config.field, Roo.form);
14396     }
14397     config = config || {};
14398     
14399     
14400     this.addEvents({
14401         /**
14402          * @event beforenodeedit
14403          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
14404          * false from the handler of this event.
14405          * @param {Editor} this
14406          * @param {Roo.tree.Node} node 
14407          */
14408         "beforenodeedit" : true
14409     });
14410     
14411     //Roo.log(config);
14412     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
14413
14414     this.tree = tree;
14415
14416     tree.on('beforeclick', this.beforeNodeClick, this);
14417     tree.getTreeEl().on('mousedown', this.hide, this);
14418     this.on('complete', this.updateNode, this);
14419     this.on('beforestartedit', this.fitToTree, this);
14420     this.on('startedit', this.bindScroll, this, {delay:10});
14421     this.on('specialkey', this.onSpecialKey, this);
14422 };
14423
14424 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
14425     /**
14426      * @cfg {String} alignment
14427      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
14428      */
14429     alignment: "l-l",
14430     // inherit
14431     autoSize: false,
14432     /**
14433      * @cfg {Boolean} hideEl
14434      * True to hide the bound element while the editor is displayed (defaults to false)
14435      */
14436     hideEl : false,
14437     /**
14438      * @cfg {String} cls
14439      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14440      */
14441     cls: "x-small-editor x-tree-editor",
14442     /**
14443      * @cfg {Boolean} shim
14444      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14445      */
14446     shim:false,
14447     // inherit
14448     shadow:"frame",
14449     /**
14450      * @cfg {Number} maxWidth
14451      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14452      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14453      * scroll and client offsets into account prior to each edit.
14454      */
14455     maxWidth: 250,
14456
14457     editDelay : 350,
14458
14459     // private
14460     fitToTree : function(ed, el){
14461         var td = this.tree.getTreeEl().dom, nd = el.dom;
14462         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14463             td.scrollLeft = nd.offsetLeft;
14464         }
14465         var w = Math.min(
14466                 this.maxWidth,
14467                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14468         this.setSize(w, '');
14469         
14470         return this.fireEvent('beforenodeedit', this, this.editNode);
14471         
14472     },
14473
14474     // private
14475     triggerEdit : function(node){
14476         this.completeEdit();
14477         this.editNode = node;
14478         this.startEdit(node.ui.textNode, node.text);
14479     },
14480
14481     // private
14482     bindScroll : function(){
14483         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14484     },
14485
14486     // private
14487     beforeNodeClick : function(node, e){
14488         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14489         this.lastClick = new Date();
14490         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14491             e.stopEvent();
14492             this.triggerEdit(node);
14493             return false;
14494         }
14495         return true;
14496     },
14497
14498     // private
14499     updateNode : function(ed, value){
14500         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14501         this.editNode.setText(value);
14502     },
14503
14504     // private
14505     onHide : function(){
14506         Roo.tree.TreeEditor.superclass.onHide.call(this);
14507         if(this.editNode){
14508             this.editNode.ui.focus();
14509         }
14510     },
14511
14512     // private
14513     onSpecialKey : function(field, e){
14514         var k = e.getKey();
14515         if(k == e.ESC){
14516             e.stopEvent();
14517             this.cancelEdit();
14518         }else if(k == e.ENTER && !e.hasModifier()){
14519             e.stopEvent();
14520             this.completeEdit();
14521         }
14522     }
14523 });//<Script type="text/javascript">
14524 /*
14525  * Based on:
14526  * Ext JS Library 1.1.1
14527  * Copyright(c) 2006-2007, Ext JS, LLC.
14528  *
14529  * Originally Released Under LGPL - original licence link has changed is not relivant.
14530  *
14531  * Fork - LGPL
14532  * <script type="text/javascript">
14533  */
14534  
14535 /**
14536  * Not documented??? - probably should be...
14537  */
14538
14539 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14540     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14541     
14542     renderElements : function(n, a, targetNode, bulkRender){
14543         //consel.log("renderElements?");
14544         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14545
14546         var t = n.getOwnerTree();
14547         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14548         
14549         var cols = t.columns;
14550         var bw = t.borderWidth;
14551         var c = cols[0];
14552         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14553          var cb = typeof a.checked == "boolean";
14554         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14555         var colcls = 'x-t-' + tid + '-c0';
14556         var buf = [
14557             '<li class="x-tree-node">',
14558             
14559                 
14560                 '<div class="x-tree-node-el ', a.cls,'">',
14561                     // extran...
14562                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14563                 
14564                 
14565                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14566                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14567                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14568                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14569                            (a.iconCls ? ' '+a.iconCls : ''),
14570                            '" unselectable="on" />',
14571                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14572                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14573                              
14574                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14575                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14576                             '<span unselectable="on" qtip="' + tx + '">',
14577                              tx,
14578                              '</span></a>' ,
14579                     '</div>',
14580                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14581                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14582                  ];
14583         for(var i = 1, len = cols.length; i < len; i++){
14584             c = cols[i];
14585             colcls = 'x-t-' + tid + '-c' +i;
14586             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14587             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14588                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14589                       "</div>");
14590          }
14591          
14592          buf.push(
14593             '</a>',
14594             '<div class="x-clear"></div></div>',
14595             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14596             "</li>");
14597         
14598         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14599             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14600                                 n.nextSibling.ui.getEl(), buf.join(""));
14601         }else{
14602             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14603         }
14604         var el = this.wrap.firstChild;
14605         this.elRow = el;
14606         this.elNode = el.firstChild;
14607         this.ranchor = el.childNodes[1];
14608         this.ctNode = this.wrap.childNodes[1];
14609         var cs = el.firstChild.childNodes;
14610         this.indentNode = cs[0];
14611         this.ecNode = cs[1];
14612         this.iconNode = cs[2];
14613         var index = 3;
14614         if(cb){
14615             this.checkbox = cs[3];
14616             index++;
14617         }
14618         this.anchor = cs[index];
14619         
14620         this.textNode = cs[index].firstChild;
14621         
14622         //el.on("click", this.onClick, this);
14623         //el.on("dblclick", this.onDblClick, this);
14624         
14625         
14626        // console.log(this);
14627     },
14628     initEvents : function(){
14629         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14630         
14631             
14632         var a = this.ranchor;
14633
14634         var el = Roo.get(a);
14635
14636         if(Roo.isOpera){ // opera render bug ignores the CSS
14637             el.setStyle("text-decoration", "none");
14638         }
14639
14640         el.on("click", this.onClick, this);
14641         el.on("dblclick", this.onDblClick, this);
14642         el.on("contextmenu", this.onContextMenu, this);
14643         
14644     },
14645     
14646     /*onSelectedChange : function(state){
14647         if(state){
14648             this.focus();
14649             this.addClass("x-tree-selected");
14650         }else{
14651             //this.blur();
14652             this.removeClass("x-tree-selected");
14653         }
14654     },*/
14655     addClass : function(cls){
14656         if(this.elRow){
14657             Roo.fly(this.elRow).addClass(cls);
14658         }
14659         
14660     },
14661     
14662     
14663     removeClass : function(cls){
14664         if(this.elRow){
14665             Roo.fly(this.elRow).removeClass(cls);
14666         }
14667     }
14668
14669     
14670     
14671 });//<Script type="text/javascript">
14672
14673 /*
14674  * Based on:
14675  * Ext JS Library 1.1.1
14676  * Copyright(c) 2006-2007, Ext JS, LLC.
14677  *
14678  * Originally Released Under LGPL - original licence link has changed is not relivant.
14679  *
14680  * Fork - LGPL
14681  * <script type="text/javascript">
14682  */
14683  
14684
14685 /**
14686  * @class Roo.tree.ColumnTree
14687  * @extends Roo.data.TreePanel
14688  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14689  * @cfg {int} borderWidth  compined right/left border allowance
14690  * @constructor
14691  * @param {String/HTMLElement/Element} el The container element
14692  * @param {Object} config
14693  */
14694 Roo.tree.ColumnTree =  function(el, config)
14695 {
14696    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14697    this.addEvents({
14698         /**
14699         * @event resize
14700         * Fire this event on a container when it resizes
14701         * @param {int} w Width
14702         * @param {int} h Height
14703         */
14704        "resize" : true
14705     });
14706     this.on('resize', this.onResize, this);
14707 };
14708
14709 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14710     //lines:false,
14711     
14712     
14713     borderWidth: Roo.isBorderBox ? 0 : 2, 
14714     headEls : false,
14715     
14716     render : function(){
14717         // add the header.....
14718        
14719         Roo.tree.ColumnTree.superclass.render.apply(this);
14720         
14721         this.el.addClass('x-column-tree');
14722         
14723         this.headers = this.el.createChild(
14724             {cls:'x-tree-headers'},this.innerCt.dom);
14725    
14726         var cols = this.columns, c;
14727         var totalWidth = 0;
14728         this.headEls = [];
14729         var  len = cols.length;
14730         for(var i = 0; i < len; i++){
14731              c = cols[i];
14732              totalWidth += c.width;
14733             this.headEls.push(this.headers.createChild({
14734                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14735                  cn: {
14736                      cls:'x-tree-hd-text',
14737                      html: c.header
14738                  },
14739                  style:'width:'+(c.width-this.borderWidth)+'px;'
14740              }));
14741         }
14742         this.headers.createChild({cls:'x-clear'});
14743         // prevent floats from wrapping when clipped
14744         this.headers.setWidth(totalWidth);
14745         //this.innerCt.setWidth(totalWidth);
14746         this.innerCt.setStyle({ overflow: 'auto' });
14747         this.onResize(this.width, this.height);
14748              
14749         
14750     },
14751     onResize : function(w,h)
14752     {
14753         this.height = h;
14754         this.width = w;
14755         // resize cols..
14756         this.innerCt.setWidth(this.width);
14757         this.innerCt.setHeight(this.height-20);
14758         
14759         // headers...
14760         var cols = this.columns, c;
14761         var totalWidth = 0;
14762         var expEl = false;
14763         var len = cols.length;
14764         for(var i = 0; i < len; i++){
14765             c = cols[i];
14766             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14767                 // it's the expander..
14768                 expEl  = this.headEls[i];
14769                 continue;
14770             }
14771             totalWidth += c.width;
14772             
14773         }
14774         if (expEl) {
14775             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14776         }
14777         this.headers.setWidth(w-20);
14778
14779         
14780         
14781         
14782     }
14783 });
14784 /*
14785  * Based on:
14786  * Ext JS Library 1.1.1
14787  * Copyright(c) 2006-2007, Ext JS, LLC.
14788  *
14789  * Originally Released Under LGPL - original licence link has changed is not relivant.
14790  *
14791  * Fork - LGPL
14792  * <script type="text/javascript">
14793  */
14794  
14795 /**
14796  * @class Roo.menu.Menu
14797  * @extends Roo.util.Observable
14798  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14799  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14800  * @constructor
14801  * Creates a new Menu
14802  * @param {Object} config Configuration options
14803  */
14804 Roo.menu.Menu = function(config){
14805     
14806     Roo.menu.Menu.superclass.constructor.call(this, config);
14807     
14808     this.id = this.id || Roo.id();
14809     this.addEvents({
14810         /**
14811          * @event beforeshow
14812          * Fires before this menu is displayed
14813          * @param {Roo.menu.Menu} this
14814          */
14815         beforeshow : true,
14816         /**
14817          * @event beforehide
14818          * Fires before this menu is hidden
14819          * @param {Roo.menu.Menu} this
14820          */
14821         beforehide : true,
14822         /**
14823          * @event show
14824          * Fires after this menu is displayed
14825          * @param {Roo.menu.Menu} this
14826          */
14827         show : true,
14828         /**
14829          * @event hide
14830          * Fires after this menu is hidden
14831          * @param {Roo.menu.Menu} this
14832          */
14833         hide : true,
14834         /**
14835          * @event click
14836          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14837          * @param {Roo.menu.Menu} this
14838          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14839          * @param {Roo.EventObject} e
14840          */
14841         click : true,
14842         /**
14843          * @event mouseover
14844          * Fires when the mouse is hovering over this menu
14845          * @param {Roo.menu.Menu} this
14846          * @param {Roo.EventObject} e
14847          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14848          */
14849         mouseover : true,
14850         /**
14851          * @event mouseout
14852          * Fires when the mouse exits this menu
14853          * @param {Roo.menu.Menu} this
14854          * @param {Roo.EventObject} e
14855          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14856          */
14857         mouseout : true,
14858         /**
14859          * @event itemclick
14860          * Fires when a menu item contained in this menu is clicked
14861          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14862          * @param {Roo.EventObject} e
14863          */
14864         itemclick: true
14865     });
14866     if (this.registerMenu) {
14867         Roo.menu.MenuMgr.register(this);
14868     }
14869     
14870     var mis = this.items;
14871     this.items = new Roo.util.MixedCollection();
14872     if(mis){
14873         this.add.apply(this, mis);
14874     }
14875 };
14876
14877 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14878     /**
14879      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14880      */
14881     minWidth : 120,
14882     /**
14883      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14884      * for bottom-right shadow (defaults to "sides")
14885      */
14886     shadow : "sides",
14887     /**
14888      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14889      * this menu (defaults to "tl-tr?")
14890      */
14891     subMenuAlign : "tl-tr?",
14892     /**
14893      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14894      * relative to its element of origin (defaults to "tl-bl?")
14895      */
14896     defaultAlign : "tl-bl?",
14897     /**
14898      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14899      */
14900     allowOtherMenus : false,
14901     /**
14902      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14903      */
14904     registerMenu : true,
14905
14906     hidden:true,
14907
14908     // private
14909     render : function(){
14910         if(this.el){
14911             return;
14912         }
14913         var el = this.el = new Roo.Layer({
14914             cls: "x-menu",
14915             shadow:this.shadow,
14916             constrain: false,
14917             parentEl: this.parentEl || document.body,
14918             zindex:15000
14919         });
14920
14921         this.keyNav = new Roo.menu.MenuNav(this);
14922
14923         if(this.plain){
14924             el.addClass("x-menu-plain");
14925         }
14926         if(this.cls){
14927             el.addClass(this.cls);
14928         }
14929         // generic focus element
14930         this.focusEl = el.createChild({
14931             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14932         });
14933         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14934         //disabling touch- as it's causing issues ..
14935         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14936         ul.on('click'   , this.onClick, this);
14937         
14938         
14939         ul.on("mouseover", this.onMouseOver, this);
14940         ul.on("mouseout", this.onMouseOut, this);
14941         this.items.each(function(item){
14942             if (item.hidden) {
14943                 return;
14944             }
14945             
14946             var li = document.createElement("li");
14947             li.className = "x-menu-list-item";
14948             ul.dom.appendChild(li);
14949             item.render(li, this);
14950         }, this);
14951         this.ul = ul;
14952         this.autoWidth();
14953     },
14954
14955     // private
14956     autoWidth : function(){
14957         var el = this.el, ul = this.ul;
14958         if(!el){
14959             return;
14960         }
14961         var w = this.width;
14962         if(w){
14963             el.setWidth(w);
14964         }else if(Roo.isIE){
14965             el.setWidth(this.minWidth);
14966             var t = el.dom.offsetWidth; // force recalc
14967             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14968         }
14969     },
14970
14971     // private
14972     delayAutoWidth : function(){
14973         if(this.rendered){
14974             if(!this.awTask){
14975                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14976             }
14977             this.awTask.delay(20);
14978         }
14979     },
14980
14981     // private
14982     findTargetItem : function(e){
14983         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14984         if(t && t.menuItemId){
14985             return this.items.get(t.menuItemId);
14986         }
14987     },
14988
14989     // private
14990     onClick : function(e){
14991         Roo.log("menu.onClick");
14992         var t = this.findTargetItem(e);
14993         if(!t){
14994             return;
14995         }
14996         Roo.log(e);
14997         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14998             if(t == this.activeItem && t.shouldDeactivate(e)){
14999                 this.activeItem.deactivate();
15000                 delete this.activeItem;
15001                 return;
15002             }
15003             if(t.canActivate){
15004                 this.setActiveItem(t, true);
15005             }
15006             return;
15007             
15008             
15009         }
15010         
15011         t.onClick(e);
15012         this.fireEvent("click", this, t, e);
15013     },
15014
15015     // private
15016     setActiveItem : function(item, autoExpand){
15017         if(item != this.activeItem){
15018             if(this.activeItem){
15019                 this.activeItem.deactivate();
15020             }
15021             this.activeItem = item;
15022             item.activate(autoExpand);
15023         }else if(autoExpand){
15024             item.expandMenu();
15025         }
15026     },
15027
15028     // private
15029     tryActivate : function(start, step){
15030         var items = this.items;
15031         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
15032             var item = items.get(i);
15033             if(!item.disabled && item.canActivate){
15034                 this.setActiveItem(item, false);
15035                 return item;
15036             }
15037         }
15038         return false;
15039     },
15040
15041     // private
15042     onMouseOver : function(e){
15043         var t;
15044         if(t = this.findTargetItem(e)){
15045             if(t.canActivate && !t.disabled){
15046                 this.setActiveItem(t, true);
15047             }
15048         }
15049         this.fireEvent("mouseover", this, e, t);
15050     },
15051
15052     // private
15053     onMouseOut : function(e){
15054         var t;
15055         if(t = this.findTargetItem(e)){
15056             if(t == this.activeItem && t.shouldDeactivate(e)){
15057                 this.activeItem.deactivate();
15058                 delete this.activeItem;
15059             }
15060         }
15061         this.fireEvent("mouseout", this, e, t);
15062     },
15063
15064     /**
15065      * Read-only.  Returns true if the menu is currently displayed, else false.
15066      * @type Boolean
15067      */
15068     isVisible : function(){
15069         return this.el && !this.hidden;
15070     },
15071
15072     /**
15073      * Displays this menu relative to another element
15074      * @param {String/HTMLElement/Roo.Element} element The element to align to
15075      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
15076      * the element (defaults to this.defaultAlign)
15077      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15078      */
15079     show : function(el, pos, parentMenu){
15080         this.parentMenu = parentMenu;
15081         if(!this.el){
15082             this.render();
15083         }
15084         this.fireEvent("beforeshow", this);
15085         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
15086     },
15087
15088     /**
15089      * Displays this menu at a specific xy position
15090      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
15091      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
15092      */
15093     showAt : function(xy, parentMenu, /* private: */_e){
15094         this.parentMenu = parentMenu;
15095         if(!this.el){
15096             this.render();
15097         }
15098         if(_e !== false){
15099             this.fireEvent("beforeshow", this);
15100             xy = this.el.adjustForConstraints(xy);
15101         }
15102         this.el.setXY(xy);
15103         this.el.show();
15104         this.hidden = false;
15105         this.focus();
15106         this.fireEvent("show", this);
15107     },
15108
15109     focus : function(){
15110         if(!this.hidden){
15111             this.doFocus.defer(50, this);
15112         }
15113     },
15114
15115     doFocus : function(){
15116         if(!this.hidden){
15117             this.focusEl.focus();
15118         }
15119     },
15120
15121     /**
15122      * Hides this menu and optionally all parent menus
15123      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
15124      */
15125     hide : function(deep){
15126         if(this.el && this.isVisible()){
15127             this.fireEvent("beforehide", this);
15128             if(this.activeItem){
15129                 this.activeItem.deactivate();
15130                 this.activeItem = null;
15131             }
15132             this.el.hide();
15133             this.hidden = true;
15134             this.fireEvent("hide", this);
15135         }
15136         if(deep === true && this.parentMenu){
15137             this.parentMenu.hide(true);
15138         }
15139     },
15140
15141     /**
15142      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
15143      * Any of the following are valid:
15144      * <ul>
15145      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
15146      * <li>An HTMLElement object which will be converted to a menu item</li>
15147      * <li>A menu item config object that will be created as a new menu item</li>
15148      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
15149      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
15150      * </ul>
15151      * Usage:
15152      * <pre><code>
15153 // Create the menu
15154 var menu = new Roo.menu.Menu();
15155
15156 // Create a menu item to add by reference
15157 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
15158
15159 // Add a bunch of items at once using different methods.
15160 // Only the last item added will be returned.
15161 var item = menu.add(
15162     menuItem,                // add existing item by ref
15163     'Dynamic Item',          // new TextItem
15164     '-',                     // new separator
15165     { text: 'Config Item' }  // new item by config
15166 );
15167 </code></pre>
15168      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
15169      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
15170      */
15171     add : function(){
15172         var a = arguments, l = a.length, item;
15173         for(var i = 0; i < l; i++){
15174             var el = a[i];
15175             if ((typeof(el) == "object") && el.xtype && el.xns) {
15176                 el = Roo.factory(el, Roo.menu);
15177             }
15178             
15179             if(el.render){ // some kind of Item
15180                 item = this.addItem(el);
15181             }else if(typeof el == "string"){ // string
15182                 if(el == "separator" || el == "-"){
15183                     item = this.addSeparator();
15184                 }else{
15185                     item = this.addText(el);
15186                 }
15187             }else if(el.tagName || el.el){ // element
15188                 item = this.addElement(el);
15189             }else if(typeof el == "object"){ // must be menu item config?
15190                 item = this.addMenuItem(el);
15191             }
15192         }
15193         return item;
15194     },
15195
15196     /**
15197      * Returns this menu's underlying {@link Roo.Element} object
15198      * @return {Roo.Element} The element
15199      */
15200     getEl : function(){
15201         if(!this.el){
15202             this.render();
15203         }
15204         return this.el;
15205     },
15206
15207     /**
15208      * Adds a separator bar to the menu
15209      * @return {Roo.menu.Item} The menu item that was added
15210      */
15211     addSeparator : function(){
15212         return this.addItem(new Roo.menu.Separator());
15213     },
15214
15215     /**
15216      * Adds an {@link Roo.Element} object to the menu
15217      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
15218      * @return {Roo.menu.Item} The menu item that was added
15219      */
15220     addElement : function(el){
15221         return this.addItem(new Roo.menu.BaseItem(el));
15222     },
15223
15224     /**
15225      * Adds an existing object based on {@link Roo.menu.Item} to the menu
15226      * @param {Roo.menu.Item} item The menu item to add
15227      * @return {Roo.menu.Item} The menu item that was added
15228      */
15229     addItem : function(item){
15230         this.items.add(item);
15231         if(this.ul){
15232             var li = document.createElement("li");
15233             li.className = "x-menu-list-item";
15234             this.ul.dom.appendChild(li);
15235             item.render(li, this);
15236             this.delayAutoWidth();
15237         }
15238         return item;
15239     },
15240
15241     /**
15242      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
15243      * @param {Object} config A MenuItem config object
15244      * @return {Roo.menu.Item} The menu item that was added
15245      */
15246     addMenuItem : function(config){
15247         if(!(config instanceof Roo.menu.Item)){
15248             if(typeof config.checked == "boolean"){ // must be check menu item config?
15249                 config = new Roo.menu.CheckItem(config);
15250             }else{
15251                 config = new Roo.menu.Item(config);
15252             }
15253         }
15254         return this.addItem(config);
15255     },
15256
15257     /**
15258      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
15259      * @param {String} text The text to display in the menu item
15260      * @return {Roo.menu.Item} The menu item that was added
15261      */
15262     addText : function(text){
15263         return this.addItem(new Roo.menu.TextItem({ text : text }));
15264     },
15265
15266     /**
15267      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
15268      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
15269      * @param {Roo.menu.Item} item The menu item to add
15270      * @return {Roo.menu.Item} The menu item that was added
15271      */
15272     insert : function(index, item){
15273         this.items.insert(index, item);
15274         if(this.ul){
15275             var li = document.createElement("li");
15276             li.className = "x-menu-list-item";
15277             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
15278             item.render(li, this);
15279             this.delayAutoWidth();
15280         }
15281         return item;
15282     },
15283
15284     /**
15285      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
15286      * @param {Roo.menu.Item} item The menu item to remove
15287      */
15288     remove : function(item){
15289         this.items.removeKey(item.id);
15290         item.destroy();
15291     },
15292
15293     /**
15294      * Removes and destroys all items in the menu
15295      */
15296     removeAll : function(){
15297         var f;
15298         while(f = this.items.first()){
15299             this.remove(f);
15300         }
15301     }
15302 });
15303
15304 // MenuNav is a private utility class used internally by the Menu
15305 Roo.menu.MenuNav = function(menu){
15306     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
15307     this.scope = this.menu = menu;
15308 };
15309
15310 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
15311     doRelay : function(e, h){
15312         var k = e.getKey();
15313         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
15314             this.menu.tryActivate(0, 1);
15315             return false;
15316         }
15317         return h.call(this.scope || this, e, this.menu);
15318     },
15319
15320     up : function(e, m){
15321         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
15322             m.tryActivate(m.items.length-1, -1);
15323         }
15324     },
15325
15326     down : function(e, m){
15327         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
15328             m.tryActivate(0, 1);
15329         }
15330     },
15331
15332     right : function(e, m){
15333         if(m.activeItem){
15334             m.activeItem.expandMenu(true);
15335         }
15336     },
15337
15338     left : function(e, m){
15339         m.hide();
15340         if(m.parentMenu && m.parentMenu.activeItem){
15341             m.parentMenu.activeItem.activate();
15342         }
15343     },
15344
15345     enter : function(e, m){
15346         if(m.activeItem){
15347             e.stopPropagation();
15348             m.activeItem.onClick(e);
15349             m.fireEvent("click", this, m.activeItem);
15350             return true;
15351         }
15352     }
15353 });/*
15354  * Based on:
15355  * Ext JS Library 1.1.1
15356  * Copyright(c) 2006-2007, Ext JS, LLC.
15357  *
15358  * Originally Released Under LGPL - original licence link has changed is not relivant.
15359  *
15360  * Fork - LGPL
15361  * <script type="text/javascript">
15362  */
15363  
15364 /**
15365  * @class Roo.menu.MenuMgr
15366  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
15367  * @singleton
15368  */
15369 Roo.menu.MenuMgr = function(){
15370    var menus, active, groups = {}, attached = false, lastShow = new Date();
15371
15372    // private - called when first menu is created
15373    function init(){
15374        menus = {};
15375        active = new Roo.util.MixedCollection();
15376        Roo.get(document).addKeyListener(27, function(){
15377            if(active.length > 0){
15378                hideAll();
15379            }
15380        });
15381    }
15382
15383    // private
15384    function hideAll(){
15385        if(active && active.length > 0){
15386            var c = active.clone();
15387            c.each(function(m){
15388                m.hide();
15389            });
15390        }
15391    }
15392
15393    // private
15394    function onHide(m){
15395        active.remove(m);
15396        if(active.length < 1){
15397            Roo.get(document).un("mousedown", onMouseDown);
15398            attached = false;
15399        }
15400    }
15401
15402    // private
15403    function onShow(m){
15404        var last = active.last();
15405        lastShow = new Date();
15406        active.add(m);
15407        if(!attached){
15408            Roo.get(document).on("mousedown", onMouseDown);
15409            attached = true;
15410        }
15411        if(m.parentMenu){
15412           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
15413           m.parentMenu.activeChild = m;
15414        }else if(last && last.isVisible()){
15415           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
15416        }
15417    }
15418
15419    // private
15420    function onBeforeHide(m){
15421        if(m.activeChild){
15422            m.activeChild.hide();
15423        }
15424        if(m.autoHideTimer){
15425            clearTimeout(m.autoHideTimer);
15426            delete m.autoHideTimer;
15427        }
15428    }
15429
15430    // private
15431    function onBeforeShow(m){
15432        var pm = m.parentMenu;
15433        if(!pm && !m.allowOtherMenus){
15434            hideAll();
15435        }else if(pm && pm.activeChild && active != m){
15436            pm.activeChild.hide();
15437        }
15438    }
15439
15440    // private
15441    function onMouseDown(e){
15442        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15443            hideAll();
15444        }
15445    }
15446
15447    // private
15448    function onBeforeCheck(mi, state){
15449        if(state){
15450            var g = groups[mi.group];
15451            for(var i = 0, l = g.length; i < l; i++){
15452                if(g[i] != mi){
15453                    g[i].setChecked(false);
15454                }
15455            }
15456        }
15457    }
15458
15459    return {
15460
15461        /**
15462         * Hides all menus that are currently visible
15463         */
15464        hideAll : function(){
15465             hideAll();  
15466        },
15467
15468        // private
15469        register : function(menu){
15470            if(!menus){
15471                init();
15472            }
15473            menus[menu.id] = menu;
15474            menu.on("beforehide", onBeforeHide);
15475            menu.on("hide", onHide);
15476            menu.on("beforeshow", onBeforeShow);
15477            menu.on("show", onShow);
15478            var g = menu.group;
15479            if(g && menu.events["checkchange"]){
15480                if(!groups[g]){
15481                    groups[g] = [];
15482                }
15483                groups[g].push(menu);
15484                menu.on("checkchange", onCheck);
15485            }
15486        },
15487
15488         /**
15489          * Returns a {@link Roo.menu.Menu} object
15490          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15491          * be used to generate and return a new Menu instance.
15492          */
15493        get : function(menu){
15494            if(typeof menu == "string"){ // menu id
15495                return menus[menu];
15496            }else if(menu.events){  // menu instance
15497                return menu;
15498            }else if(typeof menu.length == 'number'){ // array of menu items?
15499                return new Roo.menu.Menu({items:menu});
15500            }else{ // otherwise, must be a config
15501                return new Roo.menu.Menu(menu);
15502            }
15503        },
15504
15505        // private
15506        unregister : function(menu){
15507            delete menus[menu.id];
15508            menu.un("beforehide", onBeforeHide);
15509            menu.un("hide", onHide);
15510            menu.un("beforeshow", onBeforeShow);
15511            menu.un("show", onShow);
15512            var g = menu.group;
15513            if(g && menu.events["checkchange"]){
15514                groups[g].remove(menu);
15515                menu.un("checkchange", onCheck);
15516            }
15517        },
15518
15519        // private
15520        registerCheckable : function(menuItem){
15521            var g = menuItem.group;
15522            if(g){
15523                if(!groups[g]){
15524                    groups[g] = [];
15525                }
15526                groups[g].push(menuItem);
15527                menuItem.on("beforecheckchange", onBeforeCheck);
15528            }
15529        },
15530
15531        // private
15532        unregisterCheckable : function(menuItem){
15533            var g = menuItem.group;
15534            if(g){
15535                groups[g].remove(menuItem);
15536                menuItem.un("beforecheckchange", onBeforeCheck);
15537            }
15538        }
15539    };
15540 }();/*
15541  * Based on:
15542  * Ext JS Library 1.1.1
15543  * Copyright(c) 2006-2007, Ext JS, LLC.
15544  *
15545  * Originally Released Under LGPL - original licence link has changed is not relivant.
15546  *
15547  * Fork - LGPL
15548  * <script type="text/javascript">
15549  */
15550  
15551
15552 /**
15553  * @class Roo.menu.BaseItem
15554  * @extends Roo.Component
15555  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15556  * management and base configuration options shared by all menu components.
15557  * @constructor
15558  * Creates a new BaseItem
15559  * @param {Object} config Configuration options
15560  */
15561 Roo.menu.BaseItem = function(config){
15562     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15563
15564     this.addEvents({
15565         /**
15566          * @event click
15567          * Fires when this item is clicked
15568          * @param {Roo.menu.BaseItem} this
15569          * @param {Roo.EventObject} e
15570          */
15571         click: true,
15572         /**
15573          * @event activate
15574          * Fires when this item is activated
15575          * @param {Roo.menu.BaseItem} this
15576          */
15577         activate : true,
15578         /**
15579          * @event deactivate
15580          * Fires when this item is deactivated
15581          * @param {Roo.menu.BaseItem} this
15582          */
15583         deactivate : true
15584     });
15585
15586     if(this.handler){
15587         this.on("click", this.handler, this.scope, true);
15588     }
15589 };
15590
15591 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15592     /**
15593      * @cfg {Function} handler
15594      * A function that will handle the click event of this menu item (defaults to undefined)
15595      */
15596     /**
15597      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15598      */
15599     canActivate : false,
15600     
15601      /**
15602      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15603      */
15604     hidden: false,
15605     
15606     /**
15607      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15608      */
15609     activeClass : "x-menu-item-active",
15610     /**
15611      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15612      */
15613     hideOnClick : true,
15614     /**
15615      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15616      */
15617     hideDelay : 100,
15618
15619     // private
15620     ctype: "Roo.menu.BaseItem",
15621
15622     // private
15623     actionMode : "container",
15624
15625     // private
15626     render : function(container, parentMenu){
15627         this.parentMenu = parentMenu;
15628         Roo.menu.BaseItem.superclass.render.call(this, container);
15629         this.container.menuItemId = this.id;
15630     },
15631
15632     // private
15633     onRender : function(container, position){
15634         this.el = Roo.get(this.el);
15635         container.dom.appendChild(this.el.dom);
15636     },
15637
15638     // private
15639     onClick : function(e){
15640         if(!this.disabled && this.fireEvent("click", this, e) !== false
15641                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15642             this.handleClick(e);
15643         }else{
15644             e.stopEvent();
15645         }
15646     },
15647
15648     // private
15649     activate : function(){
15650         if(this.disabled){
15651             return false;
15652         }
15653         var li = this.container;
15654         li.addClass(this.activeClass);
15655         this.region = li.getRegion().adjust(2, 2, -2, -2);
15656         this.fireEvent("activate", this);
15657         return true;
15658     },
15659
15660     // private
15661     deactivate : function(){
15662         this.container.removeClass(this.activeClass);
15663         this.fireEvent("deactivate", this);
15664     },
15665
15666     // private
15667     shouldDeactivate : function(e){
15668         return !this.region || !this.region.contains(e.getPoint());
15669     },
15670
15671     // private
15672     handleClick : function(e){
15673         if(this.hideOnClick){
15674             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15675         }
15676     },
15677
15678     // private
15679     expandMenu : function(autoActivate){
15680         // do nothing
15681     },
15682
15683     // private
15684     hideMenu : function(){
15685         // do nothing
15686     }
15687 });/*
15688  * Based on:
15689  * Ext JS Library 1.1.1
15690  * Copyright(c) 2006-2007, Ext JS, LLC.
15691  *
15692  * Originally Released Under LGPL - original licence link has changed is not relivant.
15693  *
15694  * Fork - LGPL
15695  * <script type="text/javascript">
15696  */
15697  
15698 /**
15699  * @class Roo.menu.Adapter
15700  * @extends Roo.menu.BaseItem
15701  * 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.
15702  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15703  * @constructor
15704  * Creates a new Adapter
15705  * @param {Object} config Configuration options
15706  */
15707 Roo.menu.Adapter = function(component, config){
15708     Roo.menu.Adapter.superclass.constructor.call(this, config);
15709     this.component = component;
15710 };
15711 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15712     // private
15713     canActivate : true,
15714
15715     // private
15716     onRender : function(container, position){
15717         this.component.render(container);
15718         this.el = this.component.getEl();
15719     },
15720
15721     // private
15722     activate : function(){
15723         if(this.disabled){
15724             return false;
15725         }
15726         this.component.focus();
15727         this.fireEvent("activate", this);
15728         return true;
15729     },
15730
15731     // private
15732     deactivate : function(){
15733         this.fireEvent("deactivate", this);
15734     },
15735
15736     // private
15737     disable : function(){
15738         this.component.disable();
15739         Roo.menu.Adapter.superclass.disable.call(this);
15740     },
15741
15742     // private
15743     enable : function(){
15744         this.component.enable();
15745         Roo.menu.Adapter.superclass.enable.call(this);
15746     }
15747 });/*
15748  * Based on:
15749  * Ext JS Library 1.1.1
15750  * Copyright(c) 2006-2007, Ext JS, LLC.
15751  *
15752  * Originally Released Under LGPL - original licence link has changed is not relivant.
15753  *
15754  * Fork - LGPL
15755  * <script type="text/javascript">
15756  */
15757
15758 /**
15759  * @class Roo.menu.TextItem
15760  * @extends Roo.menu.BaseItem
15761  * Adds a static text string to a menu, usually used as either a heading or group separator.
15762  * Note: old style constructor with text is still supported.
15763  * 
15764  * @constructor
15765  * Creates a new TextItem
15766  * @param {Object} cfg Configuration
15767  */
15768 Roo.menu.TextItem = function(cfg){
15769     if (typeof(cfg) == 'string') {
15770         this.text = cfg;
15771     } else {
15772         Roo.apply(this,cfg);
15773     }
15774     
15775     Roo.menu.TextItem.superclass.constructor.call(this);
15776 };
15777
15778 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15779     /**
15780      * @cfg {Boolean} text Text to show on item.
15781      */
15782     text : '',
15783     
15784     /**
15785      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15786      */
15787     hideOnClick : false,
15788     /**
15789      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15790      */
15791     itemCls : "x-menu-text",
15792
15793     // private
15794     onRender : function(){
15795         var s = document.createElement("span");
15796         s.className = this.itemCls;
15797         s.innerHTML = this.text;
15798         this.el = s;
15799         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15800     }
15801 });/*
15802  * Based on:
15803  * Ext JS Library 1.1.1
15804  * Copyright(c) 2006-2007, Ext JS, LLC.
15805  *
15806  * Originally Released Under LGPL - original licence link has changed is not relivant.
15807  *
15808  * Fork - LGPL
15809  * <script type="text/javascript">
15810  */
15811
15812 /**
15813  * @class Roo.menu.Separator
15814  * @extends Roo.menu.BaseItem
15815  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15816  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15817  * @constructor
15818  * @param {Object} config Configuration options
15819  */
15820 Roo.menu.Separator = function(config){
15821     Roo.menu.Separator.superclass.constructor.call(this, config);
15822 };
15823
15824 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15825     /**
15826      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15827      */
15828     itemCls : "x-menu-sep",
15829     /**
15830      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15831      */
15832     hideOnClick : false,
15833
15834     // private
15835     onRender : function(li){
15836         var s = document.createElement("span");
15837         s.className = this.itemCls;
15838         s.innerHTML = "&#160;";
15839         this.el = s;
15840         li.addClass("x-menu-sep-li");
15841         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15842     }
15843 });/*
15844  * Based on:
15845  * Ext JS Library 1.1.1
15846  * Copyright(c) 2006-2007, Ext JS, LLC.
15847  *
15848  * Originally Released Under LGPL - original licence link has changed is not relivant.
15849  *
15850  * Fork - LGPL
15851  * <script type="text/javascript">
15852  */
15853 /**
15854  * @class Roo.menu.Item
15855  * @extends Roo.menu.BaseItem
15856  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15857  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15858  * activation and click handling.
15859  * @constructor
15860  * Creates a new Item
15861  * @param {Object} config Configuration options
15862  */
15863 Roo.menu.Item = function(config){
15864     Roo.menu.Item.superclass.constructor.call(this, config);
15865     if(this.menu){
15866         this.menu = Roo.menu.MenuMgr.get(this.menu);
15867     }
15868 };
15869 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15870     
15871     /**
15872      * @cfg {String} text
15873      * The text to show on the menu item.
15874      */
15875     text: '',
15876      /**
15877      * @cfg {String} HTML to render in menu
15878      * The text to show on the menu item (HTML version).
15879      */
15880     html: '',
15881     /**
15882      * @cfg {String} icon
15883      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15884      */
15885     icon: undefined,
15886     /**
15887      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15888      */
15889     itemCls : "x-menu-item",
15890     /**
15891      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15892      */
15893     canActivate : true,
15894     /**
15895      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15896      */
15897     showDelay: 200,
15898     // doc'd in BaseItem
15899     hideDelay: 200,
15900
15901     // private
15902     ctype: "Roo.menu.Item",
15903     
15904     // private
15905     onRender : function(container, position){
15906         var el = document.createElement("a");
15907         el.hideFocus = true;
15908         el.unselectable = "on";
15909         el.href = this.href || "#";
15910         if(this.hrefTarget){
15911             el.target = this.hrefTarget;
15912         }
15913         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15914         
15915         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15916         
15917         el.innerHTML = String.format(
15918                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15919                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15920         this.el = el;
15921         Roo.menu.Item.superclass.onRender.call(this, container, position);
15922     },
15923
15924     /**
15925      * Sets the text to display in this menu item
15926      * @param {String} text The text to display
15927      * @param {Boolean} isHTML true to indicate text is pure html.
15928      */
15929     setText : function(text, isHTML){
15930         if (isHTML) {
15931             this.html = text;
15932         } else {
15933             this.text = text;
15934             this.html = '';
15935         }
15936         if(this.rendered){
15937             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15938      
15939             this.el.update(String.format(
15940                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15941                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15942             this.parentMenu.autoWidth();
15943         }
15944     },
15945
15946     // private
15947     handleClick : function(e){
15948         if(!this.href){ // if no link defined, stop the event automatically
15949             e.stopEvent();
15950         }
15951         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15952     },
15953
15954     // private
15955     activate : function(autoExpand){
15956         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15957             this.focus();
15958             if(autoExpand){
15959                 this.expandMenu();
15960             }
15961         }
15962         return true;
15963     },
15964
15965     // private
15966     shouldDeactivate : function(e){
15967         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15968             if(this.menu && this.menu.isVisible()){
15969                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15970             }
15971             return true;
15972         }
15973         return false;
15974     },
15975
15976     // private
15977     deactivate : function(){
15978         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15979         this.hideMenu();
15980     },
15981
15982     // private
15983     expandMenu : function(autoActivate){
15984         if(!this.disabled && this.menu){
15985             clearTimeout(this.hideTimer);
15986             delete this.hideTimer;
15987             if(!this.menu.isVisible() && !this.showTimer){
15988                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15989             }else if (this.menu.isVisible() && autoActivate){
15990                 this.menu.tryActivate(0, 1);
15991             }
15992         }
15993     },
15994
15995     // private
15996     deferExpand : function(autoActivate){
15997         delete this.showTimer;
15998         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15999         if(autoActivate){
16000             this.menu.tryActivate(0, 1);
16001         }
16002     },
16003
16004     // private
16005     hideMenu : function(){
16006         clearTimeout(this.showTimer);
16007         delete this.showTimer;
16008         if(!this.hideTimer && this.menu && this.menu.isVisible()){
16009             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
16010         }
16011     },
16012
16013     // private
16014     deferHide : function(){
16015         delete this.hideTimer;
16016         this.menu.hide();
16017     }
16018 });/*
16019  * Based on:
16020  * Ext JS Library 1.1.1
16021  * Copyright(c) 2006-2007, Ext JS, LLC.
16022  *
16023  * Originally Released Under LGPL - original licence link has changed is not relivant.
16024  *
16025  * Fork - LGPL
16026  * <script type="text/javascript">
16027  */
16028  
16029 /**
16030  * @class Roo.menu.CheckItem
16031  * @extends Roo.menu.Item
16032  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
16033  * @constructor
16034  * Creates a new CheckItem
16035  * @param {Object} config Configuration options
16036  */
16037 Roo.menu.CheckItem = function(config){
16038     Roo.menu.CheckItem.superclass.constructor.call(this, config);
16039     this.addEvents({
16040         /**
16041          * @event beforecheckchange
16042          * Fires before the checked value is set, providing an opportunity to cancel if needed
16043          * @param {Roo.menu.CheckItem} this
16044          * @param {Boolean} checked The new checked value that will be set
16045          */
16046         "beforecheckchange" : true,
16047         /**
16048          * @event checkchange
16049          * Fires after the checked value has been set
16050          * @param {Roo.menu.CheckItem} this
16051          * @param {Boolean} checked The checked value that was set
16052          */
16053         "checkchange" : true
16054     });
16055     if(this.checkHandler){
16056         this.on('checkchange', this.checkHandler, this.scope);
16057     }
16058 };
16059 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
16060     /**
16061      * @cfg {String} group
16062      * All check items with the same group name will automatically be grouped into a single-select
16063      * radio button group (defaults to '')
16064      */
16065     /**
16066      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
16067      */
16068     itemCls : "x-menu-item x-menu-check-item",
16069     /**
16070      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
16071      */
16072     groupClass : "x-menu-group-item",
16073
16074     /**
16075      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
16076      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
16077      * initialized with checked = true will be rendered as checked.
16078      */
16079     checked: false,
16080
16081     // private
16082     ctype: "Roo.menu.CheckItem",
16083
16084     // private
16085     onRender : function(c){
16086         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
16087         if(this.group){
16088             this.el.addClass(this.groupClass);
16089         }
16090         Roo.menu.MenuMgr.registerCheckable(this);
16091         if(this.checked){
16092             this.checked = false;
16093             this.setChecked(true, true);
16094         }
16095     },
16096
16097     // private
16098     destroy : function(){
16099         if(this.rendered){
16100             Roo.menu.MenuMgr.unregisterCheckable(this);
16101         }
16102         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
16103     },
16104
16105     /**
16106      * Set the checked state of this item
16107      * @param {Boolean} checked The new checked value
16108      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
16109      */
16110     setChecked : function(state, suppressEvent){
16111         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
16112             if(this.container){
16113                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
16114             }
16115             this.checked = state;
16116             if(suppressEvent !== true){
16117                 this.fireEvent("checkchange", this, state);
16118             }
16119         }
16120     },
16121
16122     // private
16123     handleClick : function(e){
16124        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
16125            this.setChecked(!this.checked);
16126        }
16127        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
16128     }
16129 });/*
16130  * Based on:
16131  * Ext JS Library 1.1.1
16132  * Copyright(c) 2006-2007, Ext JS, LLC.
16133  *
16134  * Originally Released Under LGPL - original licence link has changed is not relivant.
16135  *
16136  * Fork - LGPL
16137  * <script type="text/javascript">
16138  */
16139  
16140 /**
16141  * @class Roo.menu.DateItem
16142  * @extends Roo.menu.Adapter
16143  * A menu item that wraps the {@link Roo.DatPicker} component.
16144  * @constructor
16145  * Creates a new DateItem
16146  * @param {Object} config Configuration options
16147  */
16148 Roo.menu.DateItem = function(config){
16149     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
16150     /** The Roo.DatePicker object @type Roo.DatePicker */
16151     this.picker = this.component;
16152     this.addEvents({select: true});
16153     
16154     this.picker.on("render", function(picker){
16155         picker.getEl().swallowEvent("click");
16156         picker.container.addClass("x-menu-date-item");
16157     });
16158
16159     this.picker.on("select", this.onSelect, this);
16160 };
16161
16162 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
16163     // private
16164     onSelect : function(picker, date){
16165         this.fireEvent("select", this, date, picker);
16166         Roo.menu.DateItem.superclass.handleClick.call(this);
16167     }
16168 });/*
16169  * Based on:
16170  * Ext JS Library 1.1.1
16171  * Copyright(c) 2006-2007, Ext JS, LLC.
16172  *
16173  * Originally Released Under LGPL - original licence link has changed is not relivant.
16174  *
16175  * Fork - LGPL
16176  * <script type="text/javascript">
16177  */
16178  
16179 /**
16180  * @class Roo.menu.ColorItem
16181  * @extends Roo.menu.Adapter
16182  * A menu item that wraps the {@link Roo.ColorPalette} component.
16183  * @constructor
16184  * Creates a new ColorItem
16185  * @param {Object} config Configuration options
16186  */
16187 Roo.menu.ColorItem = function(config){
16188     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
16189     /** The Roo.ColorPalette object @type Roo.ColorPalette */
16190     this.palette = this.component;
16191     this.relayEvents(this.palette, ["select"]);
16192     if(this.selectHandler){
16193         this.on('select', this.selectHandler, this.scope);
16194     }
16195 };
16196 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
16197  * Based on:
16198  * Ext JS Library 1.1.1
16199  * Copyright(c) 2006-2007, Ext JS, LLC.
16200  *
16201  * Originally Released Under LGPL - original licence link has changed is not relivant.
16202  *
16203  * Fork - LGPL
16204  * <script type="text/javascript">
16205  */
16206  
16207
16208 /**
16209  * @class Roo.menu.DateMenu
16210  * @extends Roo.menu.Menu
16211  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
16212  * @constructor
16213  * Creates a new DateMenu
16214  * @param {Object} config Configuration options
16215  */
16216 Roo.menu.DateMenu = function(config){
16217     Roo.menu.DateMenu.superclass.constructor.call(this, config);
16218     this.plain = true;
16219     var di = new Roo.menu.DateItem(config);
16220     this.add(di);
16221     /**
16222      * The {@link Roo.DatePicker} instance for this DateMenu
16223      * @type DatePicker
16224      */
16225     this.picker = di.picker;
16226     /**
16227      * @event select
16228      * @param {DatePicker} picker
16229      * @param {Date} date
16230      */
16231     this.relayEvents(di, ["select"]);
16232     this.on('beforeshow', function(){
16233         if(this.picker){
16234             this.picker.hideMonthPicker(false);
16235         }
16236     }, this);
16237 };
16238 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
16239     cls:'x-date-menu'
16240 });/*
16241  * Based on:
16242  * Ext JS Library 1.1.1
16243  * Copyright(c) 2006-2007, Ext JS, LLC.
16244  *
16245  * Originally Released Under LGPL - original licence link has changed is not relivant.
16246  *
16247  * Fork - LGPL
16248  * <script type="text/javascript">
16249  */
16250  
16251
16252 /**
16253  * @class Roo.menu.ColorMenu
16254  * @extends Roo.menu.Menu
16255  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
16256  * @constructor
16257  * Creates a new ColorMenu
16258  * @param {Object} config Configuration options
16259  */
16260 Roo.menu.ColorMenu = function(config){
16261     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
16262     this.plain = true;
16263     var ci = new Roo.menu.ColorItem(config);
16264     this.add(ci);
16265     /**
16266      * The {@link Roo.ColorPalette} instance for this ColorMenu
16267      * @type ColorPalette
16268      */
16269     this.palette = ci.palette;
16270     /**
16271      * @event select
16272      * @param {ColorPalette} palette
16273      * @param {String} color
16274      */
16275     this.relayEvents(ci, ["select"]);
16276 };
16277 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
16278  * Based on:
16279  * Ext JS Library 1.1.1
16280  * Copyright(c) 2006-2007, Ext JS, LLC.
16281  *
16282  * Originally Released Under LGPL - original licence link has changed is not relivant.
16283  *
16284  * Fork - LGPL
16285  * <script type="text/javascript">
16286  */
16287  
16288 /**
16289  * @class Roo.form.TextItem
16290  * @extends Roo.BoxComponent
16291  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16292  * @constructor
16293  * Creates a new TextItem
16294  * @param {Object} config Configuration options
16295  */
16296 Roo.form.TextItem = function(config){
16297     Roo.form.TextItem.superclass.constructor.call(this, config);
16298 };
16299
16300 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
16301     
16302     /**
16303      * @cfg {String} tag the tag for this item (default div)
16304      */
16305     tag : 'div',
16306     /**
16307      * @cfg {String} html the content for this item
16308      */
16309     html : '',
16310     
16311     getAutoCreate : function()
16312     {
16313         var cfg = {
16314             id: this.id,
16315             tag: this.tag,
16316             html: this.html,
16317             cls: 'x-form-item'
16318         };
16319         
16320         return cfg;
16321         
16322     },
16323     
16324     onRender : function(ct, position)
16325     {
16326         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
16327         
16328         if(!this.el){
16329             var cfg = this.getAutoCreate();
16330             if(!cfg.name){
16331                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16332             }
16333             if (!cfg.name.length) {
16334                 delete cfg.name;
16335             }
16336             this.el = ct.createChild(cfg, position);
16337         }
16338     }
16339     
16340 });/*
16341  * Based on:
16342  * Ext JS Library 1.1.1
16343  * Copyright(c) 2006-2007, Ext JS, LLC.
16344  *
16345  * Originally Released Under LGPL - original licence link has changed is not relivant.
16346  *
16347  * Fork - LGPL
16348  * <script type="text/javascript">
16349  */
16350  
16351 /**
16352  * @class Roo.form.Field
16353  * @extends Roo.BoxComponent
16354  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
16355  * @constructor
16356  * Creates a new Field
16357  * @param {Object} config Configuration options
16358  */
16359 Roo.form.Field = function(config){
16360     Roo.form.Field.superclass.constructor.call(this, config);
16361 };
16362
16363 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
16364     /**
16365      * @cfg {String} fieldLabel Label to use when rendering a form.
16366      */
16367        /**
16368      * @cfg {String} qtip Mouse over tip
16369      */
16370      
16371     /**
16372      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
16373      */
16374     invalidClass : "x-form-invalid",
16375     /**
16376      * @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")
16377      */
16378     invalidText : "The value in this field is invalid",
16379     /**
16380      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
16381      */
16382     focusClass : "x-form-focus",
16383     /**
16384      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
16385       automatic validation (defaults to "keyup").
16386      */
16387     validationEvent : "keyup",
16388     /**
16389      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
16390      */
16391     validateOnBlur : true,
16392     /**
16393      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
16394      */
16395     validationDelay : 250,
16396     /**
16397      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16398      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
16399      */
16400     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
16401     /**
16402      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
16403      */
16404     fieldClass : "x-form-field",
16405     /**
16406      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
16407      *<pre>
16408 Value         Description
16409 -----------   ----------------------------------------------------------------------
16410 qtip          Display a quick tip when the user hovers over the field
16411 title         Display a default browser title attribute popup
16412 under         Add a block div beneath the field containing the error text
16413 side          Add an error icon to the right of the field with a popup on hover
16414 [element id]  Add the error text directly to the innerHTML of the specified element
16415 </pre>
16416      */
16417     msgTarget : 'qtip',
16418     /**
16419      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
16420      */
16421     msgFx : 'normal',
16422
16423     /**
16424      * @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.
16425      */
16426     readOnly : false,
16427
16428     /**
16429      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16430      */
16431     disabled : false,
16432
16433     /**
16434      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16435      */
16436     inputType : undefined,
16437     
16438     /**
16439      * @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).
16440          */
16441         tabIndex : undefined,
16442         
16443     // private
16444     isFormField : true,
16445
16446     // private
16447     hasFocus : false,
16448     /**
16449      * @property {Roo.Element} fieldEl
16450      * Element Containing the rendered Field (with label etc.)
16451      */
16452     /**
16453      * @cfg {Mixed} value A value to initialize this field with.
16454      */
16455     value : undefined,
16456
16457     /**
16458      * @cfg {String} name The field's HTML name attribute.
16459      */
16460     /**
16461      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16462      */
16463     // private
16464     loadedValue : false,
16465      
16466      
16467         // private ??
16468         initComponent : function(){
16469         Roo.form.Field.superclass.initComponent.call(this);
16470         this.addEvents({
16471             /**
16472              * @event focus
16473              * Fires when this field receives input focus.
16474              * @param {Roo.form.Field} this
16475              */
16476             focus : true,
16477             /**
16478              * @event blur
16479              * Fires when this field loses input focus.
16480              * @param {Roo.form.Field} this
16481              */
16482             blur : true,
16483             /**
16484              * @event specialkey
16485              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16486              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16487              * @param {Roo.form.Field} this
16488              * @param {Roo.EventObject} e The event object
16489              */
16490             specialkey : true,
16491             /**
16492              * @event change
16493              * Fires just before the field blurs if the field value has changed.
16494              * @param {Roo.form.Field} this
16495              * @param {Mixed} newValue The new value
16496              * @param {Mixed} oldValue The original value
16497              */
16498             change : true,
16499             /**
16500              * @event invalid
16501              * Fires after the field has been marked as invalid.
16502              * @param {Roo.form.Field} this
16503              * @param {String} msg The validation message
16504              */
16505             invalid : true,
16506             /**
16507              * @event valid
16508              * Fires after the field has been validated with no errors.
16509              * @param {Roo.form.Field} this
16510              */
16511             valid : true,
16512              /**
16513              * @event keyup
16514              * Fires after the key up
16515              * @param {Roo.form.Field} this
16516              * @param {Roo.EventObject}  e The event Object
16517              */
16518             keyup : true
16519         });
16520     },
16521
16522     /**
16523      * Returns the name attribute of the field if available
16524      * @return {String} name The field name
16525      */
16526     getName: function(){
16527          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16528     },
16529
16530     // private
16531     onRender : function(ct, position){
16532         Roo.form.Field.superclass.onRender.call(this, ct, position);
16533         if(!this.el){
16534             var cfg = this.getAutoCreate();
16535             if(!cfg.name){
16536                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16537             }
16538             if (!cfg.name.length) {
16539                 delete cfg.name;
16540             }
16541             if(this.inputType){
16542                 cfg.type = this.inputType;
16543             }
16544             this.el = ct.createChild(cfg, position);
16545         }
16546         var type = this.el.dom.type;
16547         if(type){
16548             if(type == 'password'){
16549                 type = 'text';
16550             }
16551             this.el.addClass('x-form-'+type);
16552         }
16553         if(this.readOnly){
16554             this.el.dom.readOnly = true;
16555         }
16556         if(this.tabIndex !== undefined){
16557             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16558         }
16559
16560         this.el.addClass([this.fieldClass, this.cls]);
16561         this.initValue();
16562     },
16563
16564     /**
16565      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16566      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16567      * @return {Roo.form.Field} this
16568      */
16569     applyTo : function(target){
16570         this.allowDomMove = false;
16571         this.el = Roo.get(target);
16572         this.render(this.el.dom.parentNode);
16573         return this;
16574     },
16575
16576     // private
16577     initValue : function(){
16578         if(this.value !== undefined){
16579             this.setValue(this.value);
16580         }else if(this.el.dom.value.length > 0){
16581             this.setValue(this.el.dom.value);
16582         }
16583     },
16584
16585     /**
16586      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16587      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16588      */
16589     isDirty : function() {
16590         if(this.disabled) {
16591             return false;
16592         }
16593         return String(this.getValue()) !== String(this.originalValue);
16594     },
16595
16596     /**
16597      * stores the current value in loadedValue
16598      */
16599     resetHasChanged : function()
16600     {
16601         this.loadedValue = String(this.getValue());
16602     },
16603     /**
16604      * checks the current value against the 'loaded' value.
16605      * Note - will return false if 'resetHasChanged' has not been called first.
16606      */
16607     hasChanged : function()
16608     {
16609         if(this.disabled || this.readOnly) {
16610             return false;
16611         }
16612         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16613     },
16614     
16615     
16616     
16617     // private
16618     afterRender : function(){
16619         Roo.form.Field.superclass.afterRender.call(this);
16620         this.initEvents();
16621     },
16622
16623     // private
16624     fireKey : function(e){
16625         //Roo.log('field ' + e.getKey());
16626         if(e.isNavKeyPress()){
16627             this.fireEvent("specialkey", this, e);
16628         }
16629     },
16630
16631     /**
16632      * Resets the current field value to the originally loaded value and clears any validation messages
16633      */
16634     reset : function(){
16635         this.setValue(this.resetValue);
16636         this.originalValue = this.getValue();
16637         this.clearInvalid();
16638     },
16639
16640     // private
16641     initEvents : function(){
16642         // safari killled keypress - so keydown is now used..
16643         this.el.on("keydown" , this.fireKey,  this);
16644         this.el.on("focus", this.onFocus,  this);
16645         this.el.on("blur", this.onBlur,  this);
16646         this.el.relayEvent('keyup', this);
16647
16648         // reference to original value for reset
16649         this.originalValue = this.getValue();
16650         this.resetValue =  this.getValue();
16651     },
16652
16653     // private
16654     onFocus : function(){
16655         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16656             this.el.addClass(this.focusClass);
16657         }
16658         if(!this.hasFocus){
16659             this.hasFocus = true;
16660             this.startValue = this.getValue();
16661             this.fireEvent("focus", this);
16662         }
16663     },
16664
16665     beforeBlur : Roo.emptyFn,
16666
16667     // private
16668     onBlur : function(){
16669         this.beforeBlur();
16670         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16671             this.el.removeClass(this.focusClass);
16672         }
16673         this.hasFocus = false;
16674         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16675             this.validate();
16676         }
16677         var v = this.getValue();
16678         if(String(v) !== String(this.startValue)){
16679             this.fireEvent('change', this, v, this.startValue);
16680         }
16681         this.fireEvent("blur", this);
16682     },
16683
16684     /**
16685      * Returns whether or not the field value is currently valid
16686      * @param {Boolean} preventMark True to disable marking the field invalid
16687      * @return {Boolean} True if the value is valid, else false
16688      */
16689     isValid : function(preventMark){
16690         if(this.disabled){
16691             return true;
16692         }
16693         var restore = this.preventMark;
16694         this.preventMark = preventMark === true;
16695         var v = this.validateValue(this.processValue(this.getRawValue()));
16696         this.preventMark = restore;
16697         return v;
16698     },
16699
16700     /**
16701      * Validates the field value
16702      * @return {Boolean} True if the value is valid, else false
16703      */
16704     validate : function(){
16705         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16706             this.clearInvalid();
16707             return true;
16708         }
16709         return false;
16710     },
16711
16712     processValue : function(value){
16713         return value;
16714     },
16715
16716     // private
16717     // Subclasses should provide the validation implementation by overriding this
16718     validateValue : function(value){
16719         return true;
16720     },
16721
16722     /**
16723      * Mark this field as invalid
16724      * @param {String} msg The validation message
16725      */
16726     markInvalid : function(msg){
16727         if(!this.rendered || this.preventMark){ // not rendered
16728             return;
16729         }
16730         
16731         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16732         
16733         obj.el.addClass(this.invalidClass);
16734         msg = msg || this.invalidText;
16735         switch(this.msgTarget){
16736             case 'qtip':
16737                 obj.el.dom.qtip = msg;
16738                 obj.el.dom.qclass = 'x-form-invalid-tip';
16739                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16740                     Roo.QuickTips.enable();
16741                 }
16742                 break;
16743             case 'title':
16744                 this.el.dom.title = msg;
16745                 break;
16746             case 'under':
16747                 if(!this.errorEl){
16748                     var elp = this.el.findParent('.x-form-element', 5, true);
16749                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16750                     this.errorEl.setWidth(elp.getWidth(true)-20);
16751                 }
16752                 this.errorEl.update(msg);
16753                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16754                 break;
16755             case 'side':
16756                 if(!this.errorIcon){
16757                     var elp = this.el.findParent('.x-form-element', 5, true);
16758                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16759                 }
16760                 this.alignErrorIcon();
16761                 this.errorIcon.dom.qtip = msg;
16762                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16763                 this.errorIcon.show();
16764                 this.on('resize', this.alignErrorIcon, this);
16765                 break;
16766             default:
16767                 var t = Roo.getDom(this.msgTarget);
16768                 t.innerHTML = msg;
16769                 t.style.display = this.msgDisplay;
16770                 break;
16771         }
16772         this.fireEvent('invalid', this, msg);
16773     },
16774
16775     // private
16776     alignErrorIcon : function(){
16777         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16778     },
16779
16780     /**
16781      * Clear any invalid styles/messages for this field
16782      */
16783     clearInvalid : function(){
16784         if(!this.rendered || this.preventMark){ // not rendered
16785             return;
16786         }
16787         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16788         
16789         obj.el.removeClass(this.invalidClass);
16790         switch(this.msgTarget){
16791             case 'qtip':
16792                 obj.el.dom.qtip = '';
16793                 break;
16794             case 'title':
16795                 this.el.dom.title = '';
16796                 break;
16797             case 'under':
16798                 if(this.errorEl){
16799                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16800                 }
16801                 break;
16802             case 'side':
16803                 if(this.errorIcon){
16804                     this.errorIcon.dom.qtip = '';
16805                     this.errorIcon.hide();
16806                     this.un('resize', this.alignErrorIcon, this);
16807                 }
16808                 break;
16809             default:
16810                 var t = Roo.getDom(this.msgTarget);
16811                 t.innerHTML = '';
16812                 t.style.display = 'none';
16813                 break;
16814         }
16815         this.fireEvent('valid', this);
16816     },
16817
16818     /**
16819      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16820      * @return {Mixed} value The field value
16821      */
16822     getRawValue : function(){
16823         var v = this.el.getValue();
16824         
16825         return v;
16826     },
16827
16828     /**
16829      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16830      * @return {Mixed} value The field value
16831      */
16832     getValue : function(){
16833         var v = this.el.getValue();
16834          
16835         return v;
16836     },
16837
16838     /**
16839      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16840      * @param {Mixed} value The value to set
16841      */
16842     setRawValue : function(v){
16843         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16844     },
16845
16846     /**
16847      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16848      * @param {Mixed} value The value to set
16849      */
16850     setValue : function(v){
16851         this.value = v;
16852         if(this.rendered){
16853             this.el.dom.value = (v === null || v === undefined ? '' : v);
16854              this.validate();
16855         }
16856     },
16857
16858     adjustSize : function(w, h){
16859         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16860         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16861         return s;
16862     },
16863
16864     adjustWidth : function(tag, w){
16865         tag = tag.toLowerCase();
16866         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16867             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16868                 if(tag == 'input'){
16869                     return w + 2;
16870                 }
16871                 if(tag == 'textarea'){
16872                     return w-2;
16873                 }
16874             }else if(Roo.isOpera){
16875                 if(tag == 'input'){
16876                     return w + 2;
16877                 }
16878                 if(tag == 'textarea'){
16879                     return w-2;
16880                 }
16881             }
16882         }
16883         return w;
16884     }
16885 });
16886
16887
16888 // anything other than normal should be considered experimental
16889 Roo.form.Field.msgFx = {
16890     normal : {
16891         show: function(msgEl, f){
16892             msgEl.setDisplayed('block');
16893         },
16894
16895         hide : function(msgEl, f){
16896             msgEl.setDisplayed(false).update('');
16897         }
16898     },
16899
16900     slide : {
16901         show: function(msgEl, f){
16902             msgEl.slideIn('t', {stopFx:true});
16903         },
16904
16905         hide : function(msgEl, f){
16906             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16907         }
16908     },
16909
16910     slideRight : {
16911         show: function(msgEl, f){
16912             msgEl.fixDisplay();
16913             msgEl.alignTo(f.el, 'tl-tr');
16914             msgEl.slideIn('l', {stopFx:true});
16915         },
16916
16917         hide : function(msgEl, f){
16918             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16919         }
16920     }
16921 };/*
16922  * Based on:
16923  * Ext JS Library 1.1.1
16924  * Copyright(c) 2006-2007, Ext JS, LLC.
16925  *
16926  * Originally Released Under LGPL - original licence link has changed is not relivant.
16927  *
16928  * Fork - LGPL
16929  * <script type="text/javascript">
16930  */
16931  
16932
16933 /**
16934  * @class Roo.form.TextField
16935  * @extends Roo.form.Field
16936  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16937  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16938  * @constructor
16939  * Creates a new TextField
16940  * @param {Object} config Configuration options
16941  */
16942 Roo.form.TextField = function(config){
16943     Roo.form.TextField.superclass.constructor.call(this, config);
16944     this.addEvents({
16945         /**
16946          * @event autosize
16947          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16948          * according to the default logic, but this event provides a hook for the developer to apply additional
16949          * logic at runtime to resize the field if needed.
16950              * @param {Roo.form.Field} this This text field
16951              * @param {Number} width The new field width
16952              */
16953         autosize : true
16954     });
16955 };
16956
16957 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16958     /**
16959      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16960      */
16961     grow : false,
16962     /**
16963      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16964      */
16965     growMin : 30,
16966     /**
16967      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16968      */
16969     growMax : 800,
16970     /**
16971      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16972      */
16973     vtype : null,
16974     /**
16975      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16976      */
16977     maskRe : null,
16978     /**
16979      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16980      */
16981     disableKeyFilter : false,
16982     /**
16983      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16984      */
16985     allowBlank : true,
16986     /**
16987      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16988      */
16989     minLength : 0,
16990     /**
16991      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16992      */
16993     maxLength : Number.MAX_VALUE,
16994     /**
16995      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16996      */
16997     minLengthText : "The minimum length for this field is {0}",
16998     /**
16999      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
17000      */
17001     maxLengthText : "The maximum length for this field is {0}",
17002     /**
17003      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
17004      */
17005     selectOnFocus : false,
17006     /**
17007      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
17008      */    
17009     allowLeadingSpace : false,
17010     /**
17011      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
17012      */
17013     blankText : "This field is required",
17014     /**
17015      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
17016      * If available, this function will be called only after the basic validators all return true, and will be passed the
17017      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
17018      */
17019     validator : null,
17020     /**
17021      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
17022      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
17023      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
17024      */
17025     regex : null,
17026     /**
17027      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
17028      */
17029     regexText : "",
17030     /**
17031      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
17032      */
17033     emptyText : null,
17034    
17035
17036     // private
17037     initEvents : function()
17038     {
17039         if (this.emptyText) {
17040             this.el.attr('placeholder', this.emptyText);
17041         }
17042         
17043         Roo.form.TextField.superclass.initEvents.call(this);
17044         if(this.validationEvent == 'keyup'){
17045             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
17046             this.el.on('keyup', this.filterValidation, this);
17047         }
17048         else if(this.validationEvent !== false){
17049             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
17050         }
17051         
17052         if(this.selectOnFocus){
17053             this.on("focus", this.preFocus, this);
17054         }
17055         if (!this.allowLeadingSpace) {
17056             this.on('blur', this.cleanLeadingSpace, this);
17057         }
17058         
17059         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
17060             this.el.on("keypress", this.filterKeys, this);
17061         }
17062         if(this.grow){
17063             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
17064             this.el.on("click", this.autoSize,  this);
17065         }
17066         if(this.el.is('input[type=password]') && Roo.isSafari){
17067             this.el.on('keydown', this.SafariOnKeyDown, this);
17068         }
17069     },
17070
17071     processValue : function(value){
17072         if(this.stripCharsRe){
17073             var newValue = value.replace(this.stripCharsRe, '');
17074             if(newValue !== value){
17075                 this.setRawValue(newValue);
17076                 return newValue;
17077             }
17078         }
17079         return value;
17080     },
17081
17082     filterValidation : function(e){
17083         if(!e.isNavKeyPress()){
17084             this.validationTask.delay(this.validationDelay);
17085         }
17086     },
17087
17088     // private
17089     onKeyUp : function(e){
17090         if(!e.isNavKeyPress()){
17091             this.autoSize();
17092         }
17093     },
17094     // private - clean the leading white space
17095     cleanLeadingSpace : function(e)
17096     {
17097         if ( this.inputType == 'file') {
17098             return;
17099         }
17100         
17101         this.setValue((this.getValue() + '').replace(/^\s+/,''));
17102     },
17103     /**
17104      * Resets the current field value to the originally-loaded value and clears any validation messages.
17105      *  
17106      */
17107     reset : function(){
17108         Roo.form.TextField.superclass.reset.call(this);
17109        
17110     }, 
17111     // private
17112     preFocus : function(){
17113         
17114         if(this.selectOnFocus){
17115             this.el.dom.select();
17116         }
17117     },
17118
17119     
17120     // private
17121     filterKeys : function(e){
17122         var k = e.getKey();
17123         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
17124             return;
17125         }
17126         var c = e.getCharCode(), cc = String.fromCharCode(c);
17127         if(Roo.isIE && (e.isSpecialKey() || !cc)){
17128             return;
17129         }
17130         if(!this.maskRe.test(cc)){
17131             e.stopEvent();
17132         }
17133     },
17134
17135     setValue : function(v){
17136         
17137         Roo.form.TextField.superclass.setValue.apply(this, arguments);
17138         
17139         this.autoSize();
17140     },
17141
17142     /**
17143      * Validates a value according to the field's validation rules and marks the field as invalid
17144      * if the validation fails
17145      * @param {Mixed} value The value to validate
17146      * @return {Boolean} True if the value is valid, else false
17147      */
17148     validateValue : function(value){
17149         if(value.length < 1)  { // if it's blank
17150              if(this.allowBlank){
17151                 this.clearInvalid();
17152                 return true;
17153              }else{
17154                 this.markInvalid(this.blankText);
17155                 return false;
17156              }
17157         }
17158         if(value.length < this.minLength){
17159             this.markInvalid(String.format(this.minLengthText, this.minLength));
17160             return false;
17161         }
17162         if(value.length > this.maxLength){
17163             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
17164             return false;
17165         }
17166         if(this.vtype){
17167             var vt = Roo.form.VTypes;
17168             if(!vt[this.vtype](value, this)){
17169                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
17170                 return false;
17171             }
17172         }
17173         if(typeof this.validator == "function"){
17174             var msg = this.validator(value);
17175             if(msg !== true){
17176                 this.markInvalid(msg);
17177                 return false;
17178             }
17179         }
17180         if(this.regex && !this.regex.test(value)){
17181             this.markInvalid(this.regexText);
17182             return false;
17183         }
17184         return true;
17185     },
17186
17187     /**
17188      * Selects text in this field
17189      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
17190      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
17191      */
17192     selectText : function(start, end){
17193         var v = this.getRawValue();
17194         if(v.length > 0){
17195             start = start === undefined ? 0 : start;
17196             end = end === undefined ? v.length : end;
17197             var d = this.el.dom;
17198             if(d.setSelectionRange){
17199                 d.setSelectionRange(start, end);
17200             }else if(d.createTextRange){
17201                 var range = d.createTextRange();
17202                 range.moveStart("character", start);
17203                 range.moveEnd("character", v.length-end);
17204                 range.select();
17205             }
17206         }
17207     },
17208
17209     /**
17210      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
17211      * This only takes effect if grow = true, and fires the autosize event.
17212      */
17213     autoSize : function(){
17214         if(!this.grow || !this.rendered){
17215             return;
17216         }
17217         if(!this.metrics){
17218             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
17219         }
17220         var el = this.el;
17221         var v = el.dom.value;
17222         var d = document.createElement('div');
17223         d.appendChild(document.createTextNode(v));
17224         v = d.innerHTML;
17225         d = null;
17226         v += "&#160;";
17227         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
17228         this.el.setWidth(w);
17229         this.fireEvent("autosize", this, w);
17230     },
17231     
17232     // private
17233     SafariOnKeyDown : function(event)
17234     {
17235         // this is a workaround for a password hang bug on chrome/ webkit.
17236         
17237         var isSelectAll = false;
17238         
17239         if(this.el.dom.selectionEnd > 0){
17240             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
17241         }
17242         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
17243             event.preventDefault();
17244             this.setValue('');
17245             return;
17246         }
17247         
17248         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
17249             
17250             event.preventDefault();
17251             // this is very hacky as keydown always get's upper case.
17252             
17253             var cc = String.fromCharCode(event.getCharCode());
17254             
17255             
17256             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
17257             
17258         }
17259         
17260         
17261     }
17262 });/*
17263  * Based on:
17264  * Ext JS Library 1.1.1
17265  * Copyright(c) 2006-2007, Ext JS, LLC.
17266  *
17267  * Originally Released Under LGPL - original licence link has changed is not relivant.
17268  *
17269  * Fork - LGPL
17270  * <script type="text/javascript">
17271  */
17272  
17273 /**
17274  * @class Roo.form.Hidden
17275  * @extends Roo.form.TextField
17276  * Simple Hidden element used on forms 
17277  * 
17278  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
17279  * 
17280  * @constructor
17281  * Creates a new Hidden form element.
17282  * @param {Object} config Configuration options
17283  */
17284
17285
17286
17287 // easy hidden field...
17288 Roo.form.Hidden = function(config){
17289     Roo.form.Hidden.superclass.constructor.call(this, config);
17290 };
17291   
17292 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
17293     fieldLabel:      '',
17294     inputType:      'hidden',
17295     width:          50,
17296     allowBlank:     true,
17297     labelSeparator: '',
17298     hidden:         true,
17299     itemCls :       'x-form-item-display-none'
17300
17301
17302 });
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  * @class Roo.form.TriggerField
17318  * @extends Roo.form.TextField
17319  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
17320  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
17321  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
17322  * for which you can provide a custom implementation.  For example:
17323  * <pre><code>
17324 var trigger = new Roo.form.TriggerField();
17325 trigger.onTriggerClick = myTriggerFn;
17326 trigger.applyTo('my-field');
17327 </code></pre>
17328  *
17329  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
17330  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
17331  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
17332  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
17333  * @constructor
17334  * Create a new TriggerField.
17335  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
17336  * to the base TextField)
17337  */
17338 Roo.form.TriggerField = function(config){
17339     this.mimicing = false;
17340     Roo.form.TriggerField.superclass.constructor.call(this, config);
17341 };
17342
17343 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
17344     /**
17345      * @cfg {String} triggerClass A CSS class to apply to the trigger
17346      */
17347     /**
17348      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17349      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
17350      */
17351     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
17352     /**
17353      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
17354      */
17355     hideTrigger:false,
17356
17357     /** @cfg {Boolean} grow @hide */
17358     /** @cfg {Number} growMin @hide */
17359     /** @cfg {Number} growMax @hide */
17360
17361     /**
17362      * @hide 
17363      * @method
17364      */
17365     autoSize: Roo.emptyFn,
17366     // private
17367     monitorTab : true,
17368     // private
17369     deferHeight : true,
17370
17371     
17372     actionMode : 'wrap',
17373     // private
17374     onResize : function(w, h){
17375         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
17376         if(typeof w == 'number'){
17377             var x = w - this.trigger.getWidth();
17378             this.el.setWidth(this.adjustWidth('input', x));
17379             this.trigger.setStyle('left', x+'px');
17380         }
17381     },
17382
17383     // private
17384     adjustSize : Roo.BoxComponent.prototype.adjustSize,
17385
17386     // private
17387     getResizeEl : function(){
17388         return this.wrap;
17389     },
17390
17391     // private
17392     getPositionEl : function(){
17393         return this.wrap;
17394     },
17395
17396     // private
17397     alignErrorIcon : function(){
17398         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
17399     },
17400
17401     // private
17402     onRender : function(ct, position){
17403         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
17404         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
17405         this.trigger = this.wrap.createChild(this.triggerConfig ||
17406                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
17407         if(this.hideTrigger){
17408             this.trigger.setDisplayed(false);
17409         }
17410         this.initTrigger();
17411         if(!this.width){
17412             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
17413         }
17414     },
17415
17416     // private
17417     initTrigger : function(){
17418         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
17419         this.trigger.addClassOnOver('x-form-trigger-over');
17420         this.trigger.addClassOnClick('x-form-trigger-click');
17421     },
17422
17423     // private
17424     onDestroy : function(){
17425         if(this.trigger){
17426             this.trigger.removeAllListeners();
17427             this.trigger.remove();
17428         }
17429         if(this.wrap){
17430             this.wrap.remove();
17431         }
17432         Roo.form.TriggerField.superclass.onDestroy.call(this);
17433     },
17434
17435     // private
17436     onFocus : function(){
17437         Roo.form.TriggerField.superclass.onFocus.call(this);
17438         if(!this.mimicing){
17439             this.wrap.addClass('x-trigger-wrap-focus');
17440             this.mimicing = true;
17441             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17442             if(this.monitorTab){
17443                 this.el.on("keydown", this.checkTab, this);
17444             }
17445         }
17446     },
17447
17448     // private
17449     checkTab : function(e){
17450         if(e.getKey() == e.TAB){
17451             this.triggerBlur();
17452         }
17453     },
17454
17455     // private
17456     onBlur : function(){
17457         // do nothing
17458     },
17459
17460     // private
17461     mimicBlur : function(e, t){
17462         if(!this.wrap.contains(t) && this.validateBlur()){
17463             this.triggerBlur();
17464         }
17465     },
17466
17467     // private
17468     triggerBlur : function(){
17469         this.mimicing = false;
17470         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17471         if(this.monitorTab){
17472             this.el.un("keydown", this.checkTab, this);
17473         }
17474         this.wrap.removeClass('x-trigger-wrap-focus');
17475         Roo.form.TriggerField.superclass.onBlur.call(this);
17476     },
17477
17478     // private
17479     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17480     validateBlur : function(e, t){
17481         return true;
17482     },
17483
17484     // private
17485     onDisable : function(){
17486         Roo.form.TriggerField.superclass.onDisable.call(this);
17487         if(this.wrap){
17488             this.wrap.addClass('x-item-disabled');
17489         }
17490     },
17491
17492     // private
17493     onEnable : function(){
17494         Roo.form.TriggerField.superclass.onEnable.call(this);
17495         if(this.wrap){
17496             this.wrap.removeClass('x-item-disabled');
17497         }
17498     },
17499
17500     // private
17501     onShow : function(){
17502         var ae = this.getActionEl();
17503         
17504         if(ae){
17505             ae.dom.style.display = '';
17506             ae.dom.style.visibility = 'visible';
17507         }
17508     },
17509
17510     // private
17511     
17512     onHide : function(){
17513         var ae = this.getActionEl();
17514         ae.dom.style.display = 'none';
17515     },
17516
17517     /**
17518      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17519      * by an implementing function.
17520      * @method
17521      * @param {EventObject} e
17522      */
17523     onTriggerClick : Roo.emptyFn
17524 });
17525
17526 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17527 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17528 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17529 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17530     initComponent : function(){
17531         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17532
17533         this.triggerConfig = {
17534             tag:'span', cls:'x-form-twin-triggers', cn:[
17535             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17536             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17537         ]};
17538     },
17539
17540     getTrigger : function(index){
17541         return this.triggers[index];
17542     },
17543
17544     initTrigger : function(){
17545         var ts = this.trigger.select('.x-form-trigger', true);
17546         this.wrap.setStyle('overflow', 'hidden');
17547         var triggerField = this;
17548         ts.each(function(t, all, index){
17549             t.hide = function(){
17550                 var w = triggerField.wrap.getWidth();
17551                 this.dom.style.display = 'none';
17552                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17553             };
17554             t.show = function(){
17555                 var w = triggerField.wrap.getWidth();
17556                 this.dom.style.display = '';
17557                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17558             };
17559             var triggerIndex = 'Trigger'+(index+1);
17560
17561             if(this['hide'+triggerIndex]){
17562                 t.dom.style.display = 'none';
17563             }
17564             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17565             t.addClassOnOver('x-form-trigger-over');
17566             t.addClassOnClick('x-form-trigger-click');
17567         }, this);
17568         this.triggers = ts.elements;
17569     },
17570
17571     onTrigger1Click : Roo.emptyFn,
17572     onTrigger2Click : Roo.emptyFn
17573 });/*
17574  * Based on:
17575  * Ext JS Library 1.1.1
17576  * Copyright(c) 2006-2007, Ext JS, LLC.
17577  *
17578  * Originally Released Under LGPL - original licence link has changed is not relivant.
17579  *
17580  * Fork - LGPL
17581  * <script type="text/javascript">
17582  */
17583  
17584 /**
17585  * @class Roo.form.TextArea
17586  * @extends Roo.form.TextField
17587  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17588  * support for auto-sizing.
17589  * @constructor
17590  * Creates a new TextArea
17591  * @param {Object} config Configuration options
17592  */
17593 Roo.form.TextArea = function(config){
17594     Roo.form.TextArea.superclass.constructor.call(this, config);
17595     // these are provided exchanges for backwards compat
17596     // minHeight/maxHeight were replaced by growMin/growMax to be
17597     // compatible with TextField growing config values
17598     if(this.minHeight !== undefined){
17599         this.growMin = this.minHeight;
17600     }
17601     if(this.maxHeight !== undefined){
17602         this.growMax = this.maxHeight;
17603     }
17604 };
17605
17606 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17607     /**
17608      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17609      */
17610     growMin : 60,
17611     /**
17612      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17613      */
17614     growMax: 1000,
17615     /**
17616      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17617      * in the field (equivalent to setting overflow: hidden, defaults to false)
17618      */
17619     preventScrollbars: false,
17620     /**
17621      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17622      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17623      */
17624
17625     // private
17626     onRender : function(ct, position){
17627         if(!this.el){
17628             this.defaultAutoCreate = {
17629                 tag: "textarea",
17630                 style:"width:300px;height:60px;",
17631                 autocomplete: "new-password"
17632             };
17633         }
17634         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17635         if(this.grow){
17636             this.textSizeEl = Roo.DomHelper.append(document.body, {
17637                 tag: "pre", cls: "x-form-grow-sizer"
17638             });
17639             if(this.preventScrollbars){
17640                 this.el.setStyle("overflow", "hidden");
17641             }
17642             this.el.setHeight(this.growMin);
17643         }
17644     },
17645
17646     onDestroy : function(){
17647         if(this.textSizeEl){
17648             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17649         }
17650         Roo.form.TextArea.superclass.onDestroy.call(this);
17651     },
17652
17653     // private
17654     onKeyUp : function(e){
17655         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17656             this.autoSize();
17657         }
17658     },
17659
17660     /**
17661      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17662      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17663      */
17664     autoSize : function(){
17665         if(!this.grow || !this.textSizeEl){
17666             return;
17667         }
17668         var el = this.el;
17669         var v = el.dom.value;
17670         var ts = this.textSizeEl;
17671
17672         ts.innerHTML = '';
17673         ts.appendChild(document.createTextNode(v));
17674         v = ts.innerHTML;
17675
17676         Roo.fly(ts).setWidth(this.el.getWidth());
17677         if(v.length < 1){
17678             v = "&#160;&#160;";
17679         }else{
17680             if(Roo.isIE){
17681                 v = v.replace(/\n/g, '<p>&#160;</p>');
17682             }
17683             v += "&#160;\n&#160;";
17684         }
17685         ts.innerHTML = v;
17686         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17687         if(h != this.lastHeight){
17688             this.lastHeight = h;
17689             this.el.setHeight(h);
17690             this.fireEvent("autosize", this, h);
17691         }
17692     }
17693 });/*
17694  * Based on:
17695  * Ext JS Library 1.1.1
17696  * Copyright(c) 2006-2007, Ext JS, LLC.
17697  *
17698  * Originally Released Under LGPL - original licence link has changed is not relivant.
17699  *
17700  * Fork - LGPL
17701  * <script type="text/javascript">
17702  */
17703  
17704
17705 /**
17706  * @class Roo.form.NumberField
17707  * @extends Roo.form.TextField
17708  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17709  * @constructor
17710  * Creates a new NumberField
17711  * @param {Object} config Configuration options
17712  */
17713 Roo.form.NumberField = function(config){
17714     Roo.form.NumberField.superclass.constructor.call(this, config);
17715 };
17716
17717 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17718     /**
17719      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17720      */
17721     fieldClass: "x-form-field x-form-num-field",
17722     /**
17723      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17724      */
17725     allowDecimals : true,
17726     /**
17727      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17728      */
17729     decimalSeparator : ".",
17730     /**
17731      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17732      */
17733     decimalPrecision : 2,
17734     /**
17735      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17736      */
17737     allowNegative : true,
17738     /**
17739      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17740      */
17741     minValue : Number.NEGATIVE_INFINITY,
17742     /**
17743      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17744      */
17745     maxValue : Number.MAX_VALUE,
17746     /**
17747      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17748      */
17749     minText : "The minimum value for this field is {0}",
17750     /**
17751      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17752      */
17753     maxText : "The maximum value for this field is {0}",
17754     /**
17755      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17756      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17757      */
17758     nanText : "{0} is not a valid number",
17759
17760     // private
17761     initEvents : function(){
17762         Roo.form.NumberField.superclass.initEvents.call(this);
17763         var allowed = "0123456789";
17764         if(this.allowDecimals){
17765             allowed += this.decimalSeparator;
17766         }
17767         if(this.allowNegative){
17768             allowed += "-";
17769         }
17770         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17771         var keyPress = function(e){
17772             var k = e.getKey();
17773             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17774                 return;
17775             }
17776             var c = e.getCharCode();
17777             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17778                 e.stopEvent();
17779             }
17780         };
17781         this.el.on("keypress", keyPress, this);
17782     },
17783
17784     // private
17785     validateValue : function(value){
17786         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17787             return false;
17788         }
17789         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17790              return true;
17791         }
17792         var num = this.parseValue(value);
17793         if(isNaN(num)){
17794             this.markInvalid(String.format(this.nanText, value));
17795             return false;
17796         }
17797         if(num < this.minValue){
17798             this.markInvalid(String.format(this.minText, this.minValue));
17799             return false;
17800         }
17801         if(num > this.maxValue){
17802             this.markInvalid(String.format(this.maxText, this.maxValue));
17803             return false;
17804         }
17805         return true;
17806     },
17807
17808     getValue : function(){
17809         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17810     },
17811
17812     // private
17813     parseValue : function(value){
17814         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17815         return isNaN(value) ? '' : value;
17816     },
17817
17818     // private
17819     fixPrecision : function(value){
17820         var nan = isNaN(value);
17821         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17822             return nan ? '' : value;
17823         }
17824         return parseFloat(value).toFixed(this.decimalPrecision);
17825     },
17826
17827     setValue : function(v){
17828         v = this.fixPrecision(v);
17829         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17830     },
17831
17832     // private
17833     decimalPrecisionFcn : function(v){
17834         return Math.floor(v);
17835     },
17836
17837     beforeBlur : function(){
17838         var v = this.parseValue(this.getRawValue());
17839         if(v){
17840             this.setValue(v);
17841         }
17842     }
17843 });/*
17844  * Based on:
17845  * Ext JS Library 1.1.1
17846  * Copyright(c) 2006-2007, Ext JS, LLC.
17847  *
17848  * Originally Released Under LGPL - original licence link has changed is not relivant.
17849  *
17850  * Fork - LGPL
17851  * <script type="text/javascript">
17852  */
17853  
17854 /**
17855  * @class Roo.form.DateField
17856  * @extends Roo.form.TriggerField
17857  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17858 * @constructor
17859 * Create a new DateField
17860 * @param {Object} config
17861  */
17862 Roo.form.DateField = function(config)
17863 {
17864     Roo.form.DateField.superclass.constructor.call(this, config);
17865     
17866       this.addEvents({
17867          
17868         /**
17869          * @event select
17870          * Fires when a date is selected
17871              * @param {Roo.form.DateField} combo This combo box
17872              * @param {Date} date The date selected
17873              */
17874         'select' : true
17875          
17876     });
17877     
17878     
17879     if(typeof this.minValue == "string") {
17880         this.minValue = this.parseDate(this.minValue);
17881     }
17882     if(typeof this.maxValue == "string") {
17883         this.maxValue = this.parseDate(this.maxValue);
17884     }
17885     this.ddMatch = null;
17886     if(this.disabledDates){
17887         var dd = this.disabledDates;
17888         var re = "(?:";
17889         for(var i = 0; i < dd.length; i++){
17890             re += dd[i];
17891             if(i != dd.length-1) {
17892                 re += "|";
17893             }
17894         }
17895         this.ddMatch = new RegExp(re + ")");
17896     }
17897 };
17898
17899 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17900     /**
17901      * @cfg {String} format
17902      * The default date format string which can be overriden for localization support.  The format must be
17903      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17904      */
17905     format : "m/d/y",
17906     /**
17907      * @cfg {String} altFormats
17908      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17909      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17910      */
17911     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17912     /**
17913      * @cfg {Array} disabledDays
17914      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17915      */
17916     disabledDays : null,
17917     /**
17918      * @cfg {String} disabledDaysText
17919      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17920      */
17921     disabledDaysText : "Disabled",
17922     /**
17923      * @cfg {Array} disabledDates
17924      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17925      * expression so they are very powerful. Some examples:
17926      * <ul>
17927      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17928      * <li>["03/08", "09/16"] would disable those days for every year</li>
17929      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17930      * <li>["03/../2006"] would disable every day in March 2006</li>
17931      * <li>["^03"] would disable every day in every March</li>
17932      * </ul>
17933      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17934      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17935      */
17936     disabledDates : null,
17937     /**
17938      * @cfg {String} disabledDatesText
17939      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17940      */
17941     disabledDatesText : "Disabled",
17942     /**
17943      * @cfg {Date/String} minValue
17944      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17945      * valid format (defaults to null).
17946      */
17947     minValue : null,
17948     /**
17949      * @cfg {Date/String} maxValue
17950      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17951      * valid format (defaults to null).
17952      */
17953     maxValue : null,
17954     /**
17955      * @cfg {String} minText
17956      * The error text to display when the date in the cell is before minValue (defaults to
17957      * 'The date in this field must be after {minValue}').
17958      */
17959     minText : "The date in this field must be equal to or after {0}",
17960     /**
17961      * @cfg {String} maxText
17962      * The error text to display when the date in the cell is after maxValue (defaults to
17963      * 'The date in this field must be before {maxValue}').
17964      */
17965     maxText : "The date in this field must be equal to or before {0}",
17966     /**
17967      * @cfg {String} invalidText
17968      * The error text to display when the date in the field is invalid (defaults to
17969      * '{value} is not a valid date - it must be in the format {format}').
17970      */
17971     invalidText : "{0} is not a valid date - it must be in the format {1}",
17972     /**
17973      * @cfg {String} triggerClass
17974      * An additional CSS class used to style the trigger button.  The trigger will always get the
17975      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17976      * which displays a calendar icon).
17977      */
17978     triggerClass : 'x-form-date-trigger',
17979     
17980
17981     /**
17982      * @cfg {Boolean} useIso
17983      * if enabled, then the date field will use a hidden field to store the 
17984      * real value as iso formated date. default (false)
17985      */ 
17986     useIso : false,
17987     /**
17988      * @cfg {String/Object} autoCreate
17989      * A DomHelper element spec, or true for a default element spec (defaults to
17990      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17991      */ 
17992     // private
17993     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17994     
17995     // private
17996     hiddenField: false,
17997     
17998     onRender : function(ct, position)
17999     {
18000         Roo.form.DateField.superclass.onRender.call(this, ct, position);
18001         if (this.useIso) {
18002             //this.el.dom.removeAttribute('name'); 
18003             Roo.log("Changing name?");
18004             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
18005             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18006                     'before', true);
18007             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18008             // prevent input submission
18009             this.hiddenName = this.name;
18010         }
18011             
18012             
18013     },
18014     
18015     // private
18016     validateValue : function(value)
18017     {
18018         value = this.formatDate(value);
18019         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
18020             Roo.log('super failed');
18021             return false;
18022         }
18023         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18024              return true;
18025         }
18026         var svalue = value;
18027         value = this.parseDate(value);
18028         if(!value){
18029             Roo.log('parse date failed' + svalue);
18030             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18031             return false;
18032         }
18033         var time = value.getTime();
18034         if(this.minValue && time < this.minValue.getTime()){
18035             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18036             return false;
18037         }
18038         if(this.maxValue && time > this.maxValue.getTime()){
18039             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18040             return false;
18041         }
18042         if(this.disabledDays){
18043             var day = value.getDay();
18044             for(var i = 0; i < this.disabledDays.length; i++) {
18045                 if(day === this.disabledDays[i]){
18046                     this.markInvalid(this.disabledDaysText);
18047                     return false;
18048                 }
18049             }
18050         }
18051         var fvalue = this.formatDate(value);
18052         if(this.ddMatch && this.ddMatch.test(fvalue)){
18053             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18054             return false;
18055         }
18056         return true;
18057     },
18058
18059     // private
18060     // Provides logic to override the default TriggerField.validateBlur which just returns true
18061     validateBlur : function(){
18062         return !this.menu || !this.menu.isVisible();
18063     },
18064     
18065     getName: function()
18066     {
18067         // returns hidden if it's set..
18068         if (!this.rendered) {return ''};
18069         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18070         
18071     },
18072
18073     /**
18074      * Returns the current date value of the date field.
18075      * @return {Date} The date value
18076      */
18077     getValue : function(){
18078         
18079         return  this.hiddenField ?
18080                 this.hiddenField.value :
18081                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
18082     },
18083
18084     /**
18085      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18086      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
18087      * (the default format used is "m/d/y").
18088      * <br />Usage:
18089      * <pre><code>
18090 //All of these calls set the same date value (May 4, 2006)
18091
18092 //Pass a date object:
18093 var dt = new Date('5/4/06');
18094 dateField.setValue(dt);
18095
18096 //Pass a date string (default format):
18097 dateField.setValue('5/4/06');
18098
18099 //Pass a date string (custom format):
18100 dateField.format = 'Y-m-d';
18101 dateField.setValue('2006-5-4');
18102 </code></pre>
18103      * @param {String/Date} date The date or valid date string
18104      */
18105     setValue : function(date){
18106         if (this.hiddenField) {
18107             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18108         }
18109         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18110         // make sure the value field is always stored as a date..
18111         this.value = this.parseDate(date);
18112         
18113         
18114     },
18115
18116     // private
18117     parseDate : function(value){
18118         if(!value || value instanceof Date){
18119             return value;
18120         }
18121         var v = Date.parseDate(value, this.format);
18122          if (!v && this.useIso) {
18123             v = Date.parseDate(value, 'Y-m-d');
18124         }
18125         if(!v && this.altFormats){
18126             if(!this.altFormatsArray){
18127                 this.altFormatsArray = this.altFormats.split("|");
18128             }
18129             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18130                 v = Date.parseDate(value, this.altFormatsArray[i]);
18131             }
18132         }
18133         return v;
18134     },
18135
18136     // private
18137     formatDate : function(date, fmt){
18138         return (!date || !(date instanceof Date)) ?
18139                date : date.dateFormat(fmt || this.format);
18140     },
18141
18142     // private
18143     menuListeners : {
18144         select: function(m, d){
18145             
18146             this.setValue(d);
18147             this.fireEvent('select', this, d);
18148         },
18149         show : function(){ // retain focus styling
18150             this.onFocus();
18151         },
18152         hide : function(){
18153             this.focus.defer(10, this);
18154             var ml = this.menuListeners;
18155             this.menu.un("select", ml.select,  this);
18156             this.menu.un("show", ml.show,  this);
18157             this.menu.un("hide", ml.hide,  this);
18158         }
18159     },
18160
18161     // private
18162     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18163     onTriggerClick : function(){
18164         if(this.disabled){
18165             return;
18166         }
18167         if(this.menu == null){
18168             this.menu = new Roo.menu.DateMenu();
18169         }
18170         Roo.apply(this.menu.picker,  {
18171             showClear: this.allowBlank,
18172             minDate : this.minValue,
18173             maxDate : this.maxValue,
18174             disabledDatesRE : this.ddMatch,
18175             disabledDatesText : this.disabledDatesText,
18176             disabledDays : this.disabledDays,
18177             disabledDaysText : this.disabledDaysText,
18178             format : this.useIso ? 'Y-m-d' : this.format,
18179             minText : String.format(this.minText, this.formatDate(this.minValue)),
18180             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18181         });
18182         this.menu.on(Roo.apply({}, this.menuListeners, {
18183             scope:this
18184         }));
18185         this.menu.picker.setValue(this.getValue() || new Date());
18186         this.menu.show(this.el, "tl-bl?");
18187     },
18188
18189     beforeBlur : function(){
18190         var v = this.parseDate(this.getRawValue());
18191         if(v){
18192             this.setValue(v);
18193         }
18194     },
18195
18196     /*@
18197      * overide
18198      * 
18199      */
18200     isDirty : function() {
18201         if(this.disabled) {
18202             return false;
18203         }
18204         
18205         if(typeof(this.startValue) === 'undefined'){
18206             return false;
18207         }
18208         
18209         return String(this.getValue()) !== String(this.startValue);
18210         
18211     },
18212     // @overide
18213     cleanLeadingSpace : function(e)
18214     {
18215        return;
18216     }
18217     
18218 });/*
18219  * Based on:
18220  * Ext JS Library 1.1.1
18221  * Copyright(c) 2006-2007, Ext JS, LLC.
18222  *
18223  * Originally Released Under LGPL - original licence link has changed is not relivant.
18224  *
18225  * Fork - LGPL
18226  * <script type="text/javascript">
18227  */
18228  
18229 /**
18230  * @class Roo.form.MonthField
18231  * @extends Roo.form.TriggerField
18232  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
18233 * @constructor
18234 * Create a new MonthField
18235 * @param {Object} config
18236  */
18237 Roo.form.MonthField = function(config){
18238     
18239     Roo.form.MonthField.superclass.constructor.call(this, config);
18240     
18241       this.addEvents({
18242          
18243         /**
18244          * @event select
18245          * Fires when a date is selected
18246              * @param {Roo.form.MonthFieeld} combo This combo box
18247              * @param {Date} date The date selected
18248              */
18249         'select' : true
18250          
18251     });
18252     
18253     
18254     if(typeof this.minValue == "string") {
18255         this.minValue = this.parseDate(this.minValue);
18256     }
18257     if(typeof this.maxValue == "string") {
18258         this.maxValue = this.parseDate(this.maxValue);
18259     }
18260     this.ddMatch = null;
18261     if(this.disabledDates){
18262         var dd = this.disabledDates;
18263         var re = "(?:";
18264         for(var i = 0; i < dd.length; i++){
18265             re += dd[i];
18266             if(i != dd.length-1) {
18267                 re += "|";
18268             }
18269         }
18270         this.ddMatch = new RegExp(re + ")");
18271     }
18272 };
18273
18274 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
18275     /**
18276      * @cfg {String} format
18277      * The default date format string which can be overriden for localization support.  The format must be
18278      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18279      */
18280     format : "M Y",
18281     /**
18282      * @cfg {String} altFormats
18283      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18284      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18285      */
18286     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
18287     /**
18288      * @cfg {Array} disabledDays
18289      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
18290      */
18291     disabledDays : [0,1,2,3,4,5,6],
18292     /**
18293      * @cfg {String} disabledDaysText
18294      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
18295      */
18296     disabledDaysText : "Disabled",
18297     /**
18298      * @cfg {Array} disabledDates
18299      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
18300      * expression so they are very powerful. Some examples:
18301      * <ul>
18302      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
18303      * <li>["03/08", "09/16"] would disable those days for every year</li>
18304      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
18305      * <li>["03/../2006"] would disable every day in March 2006</li>
18306      * <li>["^03"] would disable every day in every March</li>
18307      * </ul>
18308      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
18309      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
18310      */
18311     disabledDates : null,
18312     /**
18313      * @cfg {String} disabledDatesText
18314      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
18315      */
18316     disabledDatesText : "Disabled",
18317     /**
18318      * @cfg {Date/String} minValue
18319      * The minimum allowed date. Can be either a Javascript date object or a string date in a
18320      * valid format (defaults to null).
18321      */
18322     minValue : null,
18323     /**
18324      * @cfg {Date/String} maxValue
18325      * The maximum allowed date. Can be either a Javascript date object or a string date in a
18326      * valid format (defaults to null).
18327      */
18328     maxValue : null,
18329     /**
18330      * @cfg {String} minText
18331      * The error text to display when the date in the cell is before minValue (defaults to
18332      * 'The date in this field must be after {minValue}').
18333      */
18334     minText : "The date in this field must be equal to or after {0}",
18335     /**
18336      * @cfg {String} maxTextf
18337      * The error text to display when the date in the cell is after maxValue (defaults to
18338      * 'The date in this field must be before {maxValue}').
18339      */
18340     maxText : "The date in this field must be equal to or before {0}",
18341     /**
18342      * @cfg {String} invalidText
18343      * The error text to display when the date in the field is invalid (defaults to
18344      * '{value} is not a valid date - it must be in the format {format}').
18345      */
18346     invalidText : "{0} is not a valid date - it must be in the format {1}",
18347     /**
18348      * @cfg {String} triggerClass
18349      * An additional CSS class used to style the trigger button.  The trigger will always get the
18350      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
18351      * which displays a calendar icon).
18352      */
18353     triggerClass : 'x-form-date-trigger',
18354     
18355
18356     /**
18357      * @cfg {Boolean} useIso
18358      * if enabled, then the date field will use a hidden field to store the 
18359      * real value as iso formated date. default (true)
18360      */ 
18361     useIso : true,
18362     /**
18363      * @cfg {String/Object} autoCreate
18364      * A DomHelper element spec, or true for a default element spec (defaults to
18365      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
18366      */ 
18367     // private
18368     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
18369     
18370     // private
18371     hiddenField: false,
18372     
18373     hideMonthPicker : false,
18374     
18375     onRender : function(ct, position)
18376     {
18377         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
18378         if (this.useIso) {
18379             this.el.dom.removeAttribute('name'); 
18380             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
18381                     'before', true);
18382             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
18383             // prevent input submission
18384             this.hiddenName = this.name;
18385         }
18386             
18387             
18388     },
18389     
18390     // private
18391     validateValue : function(value)
18392     {
18393         value = this.formatDate(value);
18394         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
18395             return false;
18396         }
18397         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
18398              return true;
18399         }
18400         var svalue = value;
18401         value = this.parseDate(value);
18402         if(!value){
18403             this.markInvalid(String.format(this.invalidText, svalue, this.format));
18404             return false;
18405         }
18406         var time = value.getTime();
18407         if(this.minValue && time < this.minValue.getTime()){
18408             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
18409             return false;
18410         }
18411         if(this.maxValue && time > this.maxValue.getTime()){
18412             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
18413             return false;
18414         }
18415         /*if(this.disabledDays){
18416             var day = value.getDay();
18417             for(var i = 0; i < this.disabledDays.length; i++) {
18418                 if(day === this.disabledDays[i]){
18419                     this.markInvalid(this.disabledDaysText);
18420                     return false;
18421                 }
18422             }
18423         }
18424         */
18425         var fvalue = this.formatDate(value);
18426         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18427             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18428             return false;
18429         }
18430         */
18431         return true;
18432     },
18433
18434     // private
18435     // Provides logic to override the default TriggerField.validateBlur which just returns true
18436     validateBlur : function(){
18437         return !this.menu || !this.menu.isVisible();
18438     },
18439
18440     /**
18441      * Returns the current date value of the date field.
18442      * @return {Date} The date value
18443      */
18444     getValue : function(){
18445         
18446         
18447         
18448         return  this.hiddenField ?
18449                 this.hiddenField.value :
18450                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18451     },
18452
18453     /**
18454      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18455      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18456      * (the default format used is "m/d/y").
18457      * <br />Usage:
18458      * <pre><code>
18459 //All of these calls set the same date value (May 4, 2006)
18460
18461 //Pass a date object:
18462 var dt = new Date('5/4/06');
18463 monthField.setValue(dt);
18464
18465 //Pass a date string (default format):
18466 monthField.setValue('5/4/06');
18467
18468 //Pass a date string (custom format):
18469 monthField.format = 'Y-m-d';
18470 monthField.setValue('2006-5-4');
18471 </code></pre>
18472      * @param {String/Date} date The date or valid date string
18473      */
18474     setValue : function(date){
18475         Roo.log('month setValue' + date);
18476         // can only be first of month..
18477         
18478         var val = this.parseDate(date);
18479         
18480         if (this.hiddenField) {
18481             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18482         }
18483         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18484         this.value = this.parseDate(date);
18485     },
18486
18487     // private
18488     parseDate : function(value){
18489         if(!value || value instanceof Date){
18490             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18491             return value;
18492         }
18493         var v = Date.parseDate(value, this.format);
18494         if (!v && this.useIso) {
18495             v = Date.parseDate(value, 'Y-m-d');
18496         }
18497         if (v) {
18498             // 
18499             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18500         }
18501         
18502         
18503         if(!v && this.altFormats){
18504             if(!this.altFormatsArray){
18505                 this.altFormatsArray = this.altFormats.split("|");
18506             }
18507             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18508                 v = Date.parseDate(value, this.altFormatsArray[i]);
18509             }
18510         }
18511         return v;
18512     },
18513
18514     // private
18515     formatDate : function(date, fmt){
18516         return (!date || !(date instanceof Date)) ?
18517                date : date.dateFormat(fmt || this.format);
18518     },
18519
18520     // private
18521     menuListeners : {
18522         select: function(m, d){
18523             this.setValue(d);
18524             this.fireEvent('select', this, d);
18525         },
18526         show : function(){ // retain focus styling
18527             this.onFocus();
18528         },
18529         hide : function(){
18530             this.focus.defer(10, this);
18531             var ml = this.menuListeners;
18532             this.menu.un("select", ml.select,  this);
18533             this.menu.un("show", ml.show,  this);
18534             this.menu.un("hide", ml.hide,  this);
18535         }
18536     },
18537     // private
18538     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18539     onTriggerClick : function(){
18540         if(this.disabled){
18541             return;
18542         }
18543         if(this.menu == null){
18544             this.menu = new Roo.menu.DateMenu();
18545            
18546         }
18547         
18548         Roo.apply(this.menu.picker,  {
18549             
18550             showClear: this.allowBlank,
18551             minDate : this.minValue,
18552             maxDate : this.maxValue,
18553             disabledDatesRE : this.ddMatch,
18554             disabledDatesText : this.disabledDatesText,
18555             
18556             format : this.useIso ? 'Y-m-d' : this.format,
18557             minText : String.format(this.minText, this.formatDate(this.minValue)),
18558             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18559             
18560         });
18561          this.menu.on(Roo.apply({}, this.menuListeners, {
18562             scope:this
18563         }));
18564        
18565         
18566         var m = this.menu;
18567         var p = m.picker;
18568         
18569         // hide month picker get's called when we called by 'before hide';
18570         
18571         var ignorehide = true;
18572         p.hideMonthPicker  = function(disableAnim){
18573             if (ignorehide) {
18574                 return;
18575             }
18576              if(this.monthPicker){
18577                 Roo.log("hideMonthPicker called");
18578                 if(disableAnim === true){
18579                     this.monthPicker.hide();
18580                 }else{
18581                     this.monthPicker.slideOut('t', {duration:.2});
18582                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18583                     p.fireEvent("select", this, this.value);
18584                     m.hide();
18585                 }
18586             }
18587         }
18588         
18589         Roo.log('picker set value');
18590         Roo.log(this.getValue());
18591         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18592         m.show(this.el, 'tl-bl?');
18593         ignorehide  = false;
18594         // this will trigger hideMonthPicker..
18595         
18596         
18597         // hidden the day picker
18598         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18599         
18600         
18601         
18602       
18603         
18604         p.showMonthPicker.defer(100, p);
18605     
18606         
18607        
18608     },
18609
18610     beforeBlur : function(){
18611         var v = this.parseDate(this.getRawValue());
18612         if(v){
18613             this.setValue(v);
18614         }
18615     }
18616
18617     /** @cfg {Boolean} grow @hide */
18618     /** @cfg {Number} growMin @hide */
18619     /** @cfg {Number} growMax @hide */
18620     /**
18621      * @hide
18622      * @method autoSize
18623      */
18624 });/*
18625  * Based on:
18626  * Ext JS Library 1.1.1
18627  * Copyright(c) 2006-2007, Ext JS, LLC.
18628  *
18629  * Originally Released Under LGPL - original licence link has changed is not relivant.
18630  *
18631  * Fork - LGPL
18632  * <script type="text/javascript">
18633  */
18634  
18635
18636 /**
18637  * @class Roo.form.ComboBox
18638  * @extends Roo.form.TriggerField
18639  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18640  * @constructor
18641  * Create a new ComboBox.
18642  * @param {Object} config Configuration options
18643  */
18644 Roo.form.ComboBox = function(config){
18645     Roo.form.ComboBox.superclass.constructor.call(this, config);
18646     this.addEvents({
18647         /**
18648          * @event expand
18649          * Fires when the dropdown list is expanded
18650              * @param {Roo.form.ComboBox} combo This combo box
18651              */
18652         'expand' : true,
18653         /**
18654          * @event collapse
18655          * Fires when the dropdown list is collapsed
18656              * @param {Roo.form.ComboBox} combo This combo box
18657              */
18658         'collapse' : true,
18659         /**
18660          * @event beforeselect
18661          * Fires before a list item is selected. Return false to cancel the selection.
18662              * @param {Roo.form.ComboBox} combo This combo box
18663              * @param {Roo.data.Record} record The data record returned from the underlying store
18664              * @param {Number} index The index of the selected item in the dropdown list
18665              */
18666         'beforeselect' : true,
18667         /**
18668          * @event select
18669          * Fires when a list item is selected
18670              * @param {Roo.form.ComboBox} combo This combo box
18671              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18672              * @param {Number} index The index of the selected item in the dropdown list
18673              */
18674         'select' : true,
18675         /**
18676          * @event beforequery
18677          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18678          * The event object passed has these properties:
18679              * @param {Roo.form.ComboBox} combo This combo box
18680              * @param {String} query The query
18681              * @param {Boolean} forceAll true to force "all" query
18682              * @param {Boolean} cancel true to cancel the query
18683              * @param {Object} e The query event object
18684              */
18685         'beforequery': true,
18686          /**
18687          * @event add
18688          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18689              * @param {Roo.form.ComboBox} combo This combo box
18690              */
18691         'add' : true,
18692         /**
18693          * @event edit
18694          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18695              * @param {Roo.form.ComboBox} combo This combo box
18696              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18697              */
18698         'edit' : true
18699         
18700         
18701     });
18702     if(this.transform){
18703         this.allowDomMove = false;
18704         var s = Roo.getDom(this.transform);
18705         if(!this.hiddenName){
18706             this.hiddenName = s.name;
18707         }
18708         if(!this.store){
18709             this.mode = 'local';
18710             var d = [], opts = s.options;
18711             for(var i = 0, len = opts.length;i < len; i++){
18712                 var o = opts[i];
18713                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18714                 if(o.selected) {
18715                     this.value = value;
18716                 }
18717                 d.push([value, o.text]);
18718             }
18719             this.store = new Roo.data.SimpleStore({
18720                 'id': 0,
18721                 fields: ['value', 'text'],
18722                 data : d
18723             });
18724             this.valueField = 'value';
18725             this.displayField = 'text';
18726         }
18727         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18728         if(!this.lazyRender){
18729             this.target = true;
18730             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18731             s.parentNode.removeChild(s); // remove it
18732             this.render(this.el.parentNode);
18733         }else{
18734             s.parentNode.removeChild(s); // remove it
18735         }
18736
18737     }
18738     if (this.store) {
18739         this.store = Roo.factory(this.store, Roo.data);
18740     }
18741     
18742     this.selectedIndex = -1;
18743     if(this.mode == 'local'){
18744         if(config.queryDelay === undefined){
18745             this.queryDelay = 10;
18746         }
18747         if(config.minChars === undefined){
18748             this.minChars = 0;
18749         }
18750     }
18751 };
18752
18753 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18754     /**
18755      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18756      */
18757     /**
18758      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18759      * rendering into an Roo.Editor, defaults to false)
18760      */
18761     /**
18762      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18763      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18764      */
18765     /**
18766      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18767      */
18768     /**
18769      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18770      * the dropdown list (defaults to undefined, with no header element)
18771      */
18772
18773      /**
18774      * @cfg {String/Roo.Template} tpl The template to use to render the output
18775      */
18776      
18777     // private
18778     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18779     /**
18780      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18781      */
18782     listWidth: undefined,
18783     /**
18784      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18785      * mode = 'remote' or 'text' if mode = 'local')
18786      */
18787     displayField: undefined,
18788     /**
18789      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18790      * mode = 'remote' or 'value' if mode = 'local'). 
18791      * Note: use of a valueField requires the user make a selection
18792      * in order for a value to be mapped.
18793      */
18794     valueField: undefined,
18795     
18796     
18797     /**
18798      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18799      * field's data value (defaults to the underlying DOM element's name)
18800      */
18801     hiddenName: undefined,
18802     /**
18803      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18804      */
18805     listClass: '',
18806     /**
18807      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18808      */
18809     selectedClass: 'x-combo-selected',
18810     /**
18811      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18812      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18813      * which displays a downward arrow icon).
18814      */
18815     triggerClass : 'x-form-arrow-trigger',
18816     /**
18817      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18818      */
18819     shadow:'sides',
18820     /**
18821      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18822      * anchor positions (defaults to 'tl-bl')
18823      */
18824     listAlign: 'tl-bl?',
18825     /**
18826      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18827      */
18828     maxHeight: 300,
18829     /**
18830      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18831      * query specified by the allQuery config option (defaults to 'query')
18832      */
18833     triggerAction: 'query',
18834     /**
18835      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18836      * (defaults to 4, does not apply if editable = false)
18837      */
18838     minChars : 4,
18839     /**
18840      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18841      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18842      */
18843     typeAhead: false,
18844     /**
18845      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18846      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18847      */
18848     queryDelay: 500,
18849     /**
18850      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18851      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18852      */
18853     pageSize: 0,
18854     /**
18855      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18856      * when editable = true (defaults to false)
18857      */
18858     selectOnFocus:false,
18859     /**
18860      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18861      */
18862     queryParam: 'query',
18863     /**
18864      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18865      * when mode = 'remote' (defaults to 'Loading...')
18866      */
18867     loadingText: 'Loading...',
18868     /**
18869      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18870      */
18871     resizable: false,
18872     /**
18873      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18874      */
18875     handleHeight : 8,
18876     /**
18877      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18878      * traditional select (defaults to true)
18879      */
18880     editable: true,
18881     /**
18882      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18883      */
18884     allQuery: '',
18885     /**
18886      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18887      */
18888     mode: 'remote',
18889     /**
18890      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18891      * listWidth has a higher value)
18892      */
18893     minListWidth : 70,
18894     /**
18895      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18896      * allow the user to set arbitrary text into the field (defaults to false)
18897      */
18898     forceSelection:false,
18899     /**
18900      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18901      * if typeAhead = true (defaults to 250)
18902      */
18903     typeAheadDelay : 250,
18904     /**
18905      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18906      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18907      */
18908     valueNotFoundText : undefined,
18909     /**
18910      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18911      */
18912     blockFocus : false,
18913     
18914     /**
18915      * @cfg {Boolean} disableClear Disable showing of clear button.
18916      */
18917     disableClear : false,
18918     /**
18919      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18920      */
18921     alwaysQuery : false,
18922     
18923     //private
18924     addicon : false,
18925     editicon: false,
18926     
18927     // element that contains real text value.. (when hidden is used..)
18928      
18929     // private
18930     onRender : function(ct, position)
18931     {
18932         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18933         
18934         if(this.hiddenName){
18935             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18936                     'before', true);
18937             this.hiddenField.value =
18938                 this.hiddenValue !== undefined ? this.hiddenValue :
18939                 this.value !== undefined ? this.value : '';
18940
18941             // prevent input submission
18942             this.el.dom.removeAttribute('name');
18943              
18944              
18945         }
18946         
18947         if(Roo.isGecko){
18948             this.el.dom.setAttribute('autocomplete', 'off');
18949         }
18950
18951         var cls = 'x-combo-list';
18952
18953         this.list = new Roo.Layer({
18954             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18955         });
18956
18957         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18958         this.list.setWidth(lw);
18959         this.list.swallowEvent('mousewheel');
18960         this.assetHeight = 0;
18961
18962         if(this.title){
18963             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18964             this.assetHeight += this.header.getHeight();
18965         }
18966
18967         this.innerList = this.list.createChild({cls:cls+'-inner'});
18968         this.innerList.on('mouseover', this.onViewOver, this);
18969         this.innerList.on('mousemove', this.onViewMove, this);
18970         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18971         
18972         if(this.allowBlank && !this.pageSize && !this.disableClear){
18973             this.footer = this.list.createChild({cls:cls+'-ft'});
18974             this.pageTb = new Roo.Toolbar(this.footer);
18975            
18976         }
18977         if(this.pageSize){
18978             this.footer = this.list.createChild({cls:cls+'-ft'});
18979             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18980                     {pageSize: this.pageSize});
18981             
18982         }
18983         
18984         if (this.pageTb && this.allowBlank && !this.disableClear) {
18985             var _this = this;
18986             this.pageTb.add(new Roo.Toolbar.Fill(), {
18987                 cls: 'x-btn-icon x-btn-clear',
18988                 text: '&#160;',
18989                 handler: function()
18990                 {
18991                     _this.collapse();
18992                     _this.clearValue();
18993                     _this.onSelect(false, -1);
18994                 }
18995             });
18996         }
18997         if (this.footer) {
18998             this.assetHeight += this.footer.getHeight();
18999         }
19000         
19001
19002         if(!this.tpl){
19003             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
19004         }
19005
19006         this.view = new Roo.View(this.innerList, this.tpl, {
19007             singleSelect:true,
19008             store: this.store,
19009             selectedClass: this.selectedClass
19010         });
19011
19012         this.view.on('click', this.onViewClick, this);
19013
19014         this.store.on('beforeload', this.onBeforeLoad, this);
19015         this.store.on('load', this.onLoad, this);
19016         this.store.on('loadexception', this.onLoadException, this);
19017
19018         if(this.resizable){
19019             this.resizer = new Roo.Resizable(this.list,  {
19020                pinned:true, handles:'se'
19021             });
19022             this.resizer.on('resize', function(r, w, h){
19023                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
19024                 this.listWidth = w;
19025                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
19026                 this.restrictHeight();
19027             }, this);
19028             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
19029         }
19030         if(!this.editable){
19031             this.editable = true;
19032             this.setEditable(false);
19033         }  
19034         
19035         
19036         if (typeof(this.events.add.listeners) != 'undefined') {
19037             
19038             this.addicon = this.wrap.createChild(
19039                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
19040        
19041             this.addicon.on('click', function(e) {
19042                 this.fireEvent('add', this);
19043             }, this);
19044         }
19045         if (typeof(this.events.edit.listeners) != 'undefined') {
19046             
19047             this.editicon = this.wrap.createChild(
19048                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
19049             if (this.addicon) {
19050                 this.editicon.setStyle('margin-left', '40px');
19051             }
19052             this.editicon.on('click', function(e) {
19053                 
19054                 // we fire even  if inothing is selected..
19055                 this.fireEvent('edit', this, this.lastData );
19056                 
19057             }, this);
19058         }
19059         
19060         
19061         
19062     },
19063
19064     // private
19065     initEvents : function(){
19066         Roo.form.ComboBox.superclass.initEvents.call(this);
19067
19068         this.keyNav = new Roo.KeyNav(this.el, {
19069             "up" : function(e){
19070                 this.inKeyMode = true;
19071                 this.selectPrev();
19072             },
19073
19074             "down" : function(e){
19075                 if(!this.isExpanded()){
19076                     this.onTriggerClick();
19077                 }else{
19078                     this.inKeyMode = true;
19079                     this.selectNext();
19080                 }
19081             },
19082
19083             "enter" : function(e){
19084                 this.onViewClick();
19085                 //return true;
19086             },
19087
19088             "esc" : function(e){
19089                 this.collapse();
19090             },
19091
19092             "tab" : function(e){
19093                 this.onViewClick(false);
19094                 this.fireEvent("specialkey", this, e);
19095                 return true;
19096             },
19097
19098             scope : this,
19099
19100             doRelay : function(foo, bar, hname){
19101                 if(hname == 'down' || this.scope.isExpanded()){
19102                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
19103                 }
19104                 return true;
19105             },
19106
19107             forceKeyDown: true
19108         });
19109         this.queryDelay = Math.max(this.queryDelay || 10,
19110                 this.mode == 'local' ? 10 : 250);
19111         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
19112         if(this.typeAhead){
19113             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
19114         }
19115         if(this.editable !== false){
19116             this.el.on("keyup", this.onKeyUp, this);
19117         }
19118         if(this.forceSelection){
19119             this.on('blur', this.doForce, this);
19120         }
19121     },
19122
19123     onDestroy : function(){
19124         if(this.view){
19125             this.view.setStore(null);
19126             this.view.el.removeAllListeners();
19127             this.view.el.remove();
19128             this.view.purgeListeners();
19129         }
19130         if(this.list){
19131             this.list.destroy();
19132         }
19133         if(this.store){
19134             this.store.un('beforeload', this.onBeforeLoad, this);
19135             this.store.un('load', this.onLoad, this);
19136             this.store.un('loadexception', this.onLoadException, this);
19137         }
19138         Roo.form.ComboBox.superclass.onDestroy.call(this);
19139     },
19140
19141     // private
19142     fireKey : function(e){
19143         if(e.isNavKeyPress() && !this.list.isVisible()){
19144             this.fireEvent("specialkey", this, e);
19145         }
19146     },
19147
19148     // private
19149     onResize: function(w, h){
19150         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
19151         
19152         if(typeof w != 'number'){
19153             // we do not handle it!?!?
19154             return;
19155         }
19156         var tw = this.trigger.getWidth();
19157         tw += this.addicon ? this.addicon.getWidth() : 0;
19158         tw += this.editicon ? this.editicon.getWidth() : 0;
19159         var x = w - tw;
19160         this.el.setWidth( this.adjustWidth('input', x));
19161             
19162         this.trigger.setStyle('left', x+'px');
19163         
19164         if(this.list && this.listWidth === undefined){
19165             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
19166             this.list.setWidth(lw);
19167             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19168         }
19169         
19170     
19171         
19172     },
19173
19174     /**
19175      * Allow or prevent the user from directly editing the field text.  If false is passed,
19176      * the user will only be able to select from the items defined in the dropdown list.  This method
19177      * is the runtime equivalent of setting the 'editable' config option at config time.
19178      * @param {Boolean} value True to allow the user to directly edit the field text
19179      */
19180     setEditable : function(value){
19181         if(value == this.editable){
19182             return;
19183         }
19184         this.editable = value;
19185         if(!value){
19186             this.el.dom.setAttribute('readOnly', true);
19187             this.el.on('mousedown', this.onTriggerClick,  this);
19188             this.el.addClass('x-combo-noedit');
19189         }else{
19190             this.el.dom.setAttribute('readOnly', false);
19191             this.el.un('mousedown', this.onTriggerClick,  this);
19192             this.el.removeClass('x-combo-noedit');
19193         }
19194     },
19195
19196     // private
19197     onBeforeLoad : function(){
19198         if(!this.hasFocus){
19199             return;
19200         }
19201         this.innerList.update(this.loadingText ?
19202                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
19203         this.restrictHeight();
19204         this.selectedIndex = -1;
19205     },
19206
19207     // private
19208     onLoad : function(){
19209         if(!this.hasFocus){
19210             return;
19211         }
19212         if(this.store.getCount() > 0){
19213             this.expand();
19214             this.restrictHeight();
19215             if(this.lastQuery == this.allQuery){
19216                 if(this.editable){
19217                     this.el.dom.select();
19218                 }
19219                 if(!this.selectByValue(this.value, true)){
19220                     this.select(0, true);
19221                 }
19222             }else{
19223                 this.selectNext();
19224                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
19225                     this.taTask.delay(this.typeAheadDelay);
19226                 }
19227             }
19228         }else{
19229             this.onEmptyResults();
19230         }
19231         //this.el.focus();
19232     },
19233     // private
19234     onLoadException : function()
19235     {
19236         this.collapse();
19237         Roo.log(this.store.reader.jsonData);
19238         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
19239             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
19240         }
19241         
19242         
19243     },
19244     // private
19245     onTypeAhead : function(){
19246         if(this.store.getCount() > 0){
19247             var r = this.store.getAt(0);
19248             var newValue = r.data[this.displayField];
19249             var len = newValue.length;
19250             var selStart = this.getRawValue().length;
19251             if(selStart != len){
19252                 this.setRawValue(newValue);
19253                 this.selectText(selStart, newValue.length);
19254             }
19255         }
19256     },
19257
19258     // private
19259     onSelect : function(record, index){
19260         if(this.fireEvent('beforeselect', this, record, index) !== false){
19261             this.setFromData(index > -1 ? record.data : false);
19262             this.collapse();
19263             this.fireEvent('select', this, record, index);
19264         }
19265     },
19266
19267     /**
19268      * Returns the currently selected field value or empty string if no value is set.
19269      * @return {String} value The selected value
19270      */
19271     getValue : function(){
19272         if(this.valueField){
19273             return typeof this.value != 'undefined' ? this.value : '';
19274         }
19275         return Roo.form.ComboBox.superclass.getValue.call(this);
19276     },
19277
19278     /**
19279      * Clears any text/value currently set in the field
19280      */
19281     clearValue : function(){
19282         if(this.hiddenField){
19283             this.hiddenField.value = '';
19284         }
19285         this.value = '';
19286         this.setRawValue('');
19287         this.lastSelectionText = '';
19288         
19289     },
19290
19291     /**
19292      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
19293      * will be displayed in the field.  If the value does not match the data value of an existing item,
19294      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
19295      * Otherwise the field will be blank (although the value will still be set).
19296      * @param {String} value The value to match
19297      */
19298     setValue : function(v){
19299         var text = v;
19300         if(this.valueField){
19301             var r = this.findRecord(this.valueField, v);
19302             if(r){
19303                 text = r.data[this.displayField];
19304             }else if(this.valueNotFoundText !== undefined){
19305                 text = this.valueNotFoundText;
19306             }
19307         }
19308         this.lastSelectionText = text;
19309         if(this.hiddenField){
19310             this.hiddenField.value = v;
19311         }
19312         Roo.form.ComboBox.superclass.setValue.call(this, text);
19313         this.value = v;
19314     },
19315     /**
19316      * @property {Object} the last set data for the element
19317      */
19318     
19319     lastData : false,
19320     /**
19321      * Sets the value of the field based on a object which is related to the record format for the store.
19322      * @param {Object} value the value to set as. or false on reset?
19323      */
19324     setFromData : function(o){
19325         var dv = ''; // display value
19326         var vv = ''; // value value..
19327         this.lastData = o;
19328         if (this.displayField) {
19329             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
19330         } else {
19331             // this is an error condition!!!
19332             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
19333         }
19334         
19335         if(this.valueField){
19336             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
19337         }
19338         if(this.hiddenField){
19339             this.hiddenField.value = vv;
19340             
19341             this.lastSelectionText = dv;
19342             Roo.form.ComboBox.superclass.setValue.call(this, dv);
19343             this.value = vv;
19344             return;
19345         }
19346         // no hidden field.. - we store the value in 'value', but still display
19347         // display field!!!!
19348         this.lastSelectionText = dv;
19349         Roo.form.ComboBox.superclass.setValue.call(this, dv);
19350         this.value = vv;
19351         
19352         
19353     },
19354     // private
19355     reset : function(){
19356         // overridden so that last data is reset..
19357         this.setValue(this.resetValue);
19358         this.originalValue = this.getValue();
19359         this.clearInvalid();
19360         this.lastData = false;
19361         if (this.view) {
19362             this.view.clearSelections();
19363         }
19364     },
19365     // private
19366     findRecord : function(prop, value){
19367         var record;
19368         if(this.store.getCount() > 0){
19369             this.store.each(function(r){
19370                 if(r.data[prop] == value){
19371                     record = r;
19372                     return false;
19373                 }
19374                 return true;
19375             });
19376         }
19377         return record;
19378     },
19379     
19380     getName: function()
19381     {
19382         // returns hidden if it's set..
19383         if (!this.rendered) {return ''};
19384         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
19385         
19386     },
19387     // private
19388     onViewMove : function(e, t){
19389         this.inKeyMode = false;
19390     },
19391
19392     // private
19393     onViewOver : function(e, t){
19394         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
19395             return;
19396         }
19397         var item = this.view.findItemFromChild(t);
19398         if(item){
19399             var index = this.view.indexOf(item);
19400             this.select(index, false);
19401         }
19402     },
19403
19404     // private
19405     onViewClick : function(doFocus)
19406     {
19407         var index = this.view.getSelectedIndexes()[0];
19408         var r = this.store.getAt(index);
19409         if(r){
19410             this.onSelect(r, index);
19411         }
19412         if(doFocus !== false && !this.blockFocus){
19413             this.el.focus();
19414         }
19415     },
19416
19417     // private
19418     restrictHeight : function(){
19419         this.innerList.dom.style.height = '';
19420         var inner = this.innerList.dom;
19421         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
19422         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19423         this.list.beginUpdate();
19424         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19425         this.list.alignTo(this.el, this.listAlign);
19426         this.list.endUpdate();
19427     },
19428
19429     // private
19430     onEmptyResults : function(){
19431         this.collapse();
19432     },
19433
19434     /**
19435      * Returns true if the dropdown list is expanded, else false.
19436      */
19437     isExpanded : function(){
19438         return this.list.isVisible();
19439     },
19440
19441     /**
19442      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19443      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19444      * @param {String} value The data value of the item to select
19445      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19446      * selected item if it is not currently in view (defaults to true)
19447      * @return {Boolean} True if the value matched an item in the list, else false
19448      */
19449     selectByValue : function(v, scrollIntoView){
19450         if(v !== undefined && v !== null){
19451             var r = this.findRecord(this.valueField || this.displayField, v);
19452             if(r){
19453                 this.select(this.store.indexOf(r), scrollIntoView);
19454                 return true;
19455             }
19456         }
19457         return false;
19458     },
19459
19460     /**
19461      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19462      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19463      * @param {Number} index The zero-based index of the list item to select
19464      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19465      * selected item if it is not currently in view (defaults to true)
19466      */
19467     select : function(index, scrollIntoView){
19468         this.selectedIndex = index;
19469         this.view.select(index);
19470         if(scrollIntoView !== false){
19471             var el = this.view.getNode(index);
19472             if(el){
19473                 this.innerList.scrollChildIntoView(el, false);
19474             }
19475         }
19476     },
19477
19478     // private
19479     selectNext : function(){
19480         var ct = this.store.getCount();
19481         if(ct > 0){
19482             if(this.selectedIndex == -1){
19483                 this.select(0);
19484             }else if(this.selectedIndex < ct-1){
19485                 this.select(this.selectedIndex+1);
19486             }
19487         }
19488     },
19489
19490     // private
19491     selectPrev : function(){
19492         var ct = this.store.getCount();
19493         if(ct > 0){
19494             if(this.selectedIndex == -1){
19495                 this.select(0);
19496             }else if(this.selectedIndex != 0){
19497                 this.select(this.selectedIndex-1);
19498             }
19499         }
19500     },
19501
19502     // private
19503     onKeyUp : function(e){
19504         if(this.editable !== false && !e.isSpecialKey()){
19505             this.lastKey = e.getKey();
19506             this.dqTask.delay(this.queryDelay);
19507         }
19508     },
19509
19510     // private
19511     validateBlur : function(){
19512         return !this.list || !this.list.isVisible();   
19513     },
19514
19515     // private
19516     initQuery : function(){
19517         this.doQuery(this.getRawValue());
19518     },
19519
19520     // private
19521     doForce : function(){
19522         if(this.el.dom.value.length > 0){
19523             this.el.dom.value =
19524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19525              
19526         }
19527     },
19528
19529     /**
19530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19531      * query allowing the query action to be canceled if needed.
19532      * @param {String} query The SQL query to execute
19533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19535      * saved in the current store (defaults to false)
19536      */
19537     doQuery : function(q, forceAll){
19538         if(q === undefined || q === null){
19539             q = '';
19540         }
19541         var qe = {
19542             query: q,
19543             forceAll: forceAll,
19544             combo: this,
19545             cancel:false
19546         };
19547         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19548             return false;
19549         }
19550         q = qe.query;
19551         forceAll = qe.forceAll;
19552         if(forceAll === true || (q.length >= this.minChars)){
19553             if(this.lastQuery != q || this.alwaysQuery){
19554                 this.lastQuery = q;
19555                 if(this.mode == 'local'){
19556                     this.selectedIndex = -1;
19557                     if(forceAll){
19558                         this.store.clearFilter();
19559                     }else{
19560                         this.store.filter(this.displayField, q);
19561                     }
19562                     this.onLoad();
19563                 }else{
19564                     this.store.baseParams[this.queryParam] = q;
19565                     this.store.load({
19566                         params: this.getParams(q)
19567                     });
19568                     this.expand();
19569                 }
19570             }else{
19571                 this.selectedIndex = -1;
19572                 this.onLoad();   
19573             }
19574         }
19575     },
19576
19577     // private
19578     getParams : function(q){
19579         var p = {};
19580         //p[this.queryParam] = q;
19581         if(this.pageSize){
19582             p.start = 0;
19583             p.limit = this.pageSize;
19584         }
19585         return p;
19586     },
19587
19588     /**
19589      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19590      */
19591     collapse : function(){
19592         if(!this.isExpanded()){
19593             return;
19594         }
19595         this.list.hide();
19596         Roo.get(document).un('mousedown', this.collapseIf, this);
19597         Roo.get(document).un('mousewheel', this.collapseIf, this);
19598         if (!this.editable) {
19599             Roo.get(document).un('keydown', this.listKeyPress, this);
19600         }
19601         this.fireEvent('collapse', this);
19602     },
19603
19604     // private
19605     collapseIf : function(e){
19606         if(!e.within(this.wrap) && !e.within(this.list)){
19607             this.collapse();
19608         }
19609     },
19610
19611     /**
19612      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19613      */
19614     expand : function(){
19615         if(this.isExpanded() || !this.hasFocus){
19616             return;
19617         }
19618         this.list.alignTo(this.el, this.listAlign);
19619         this.list.show();
19620         Roo.get(document).on('mousedown', this.collapseIf, this);
19621         Roo.get(document).on('mousewheel', this.collapseIf, this);
19622         if (!this.editable) {
19623             Roo.get(document).on('keydown', this.listKeyPress, this);
19624         }
19625         
19626         this.fireEvent('expand', this);
19627     },
19628
19629     // private
19630     // Implements the default empty TriggerField.onTriggerClick function
19631     onTriggerClick : function(){
19632         if(this.disabled){
19633             return;
19634         }
19635         if(this.isExpanded()){
19636             this.collapse();
19637             if (!this.blockFocus) {
19638                 this.el.focus();
19639             }
19640             
19641         }else {
19642             this.hasFocus = true;
19643             if(this.triggerAction == 'all') {
19644                 this.doQuery(this.allQuery, true);
19645             } else {
19646                 this.doQuery(this.getRawValue());
19647             }
19648             if (!this.blockFocus) {
19649                 this.el.focus();
19650             }
19651         }
19652     },
19653     listKeyPress : function(e)
19654     {
19655         //Roo.log('listkeypress');
19656         // scroll to first matching element based on key pres..
19657         if (e.isSpecialKey()) {
19658             return false;
19659         }
19660         var k = String.fromCharCode(e.getKey()).toUpperCase();
19661         //Roo.log(k);
19662         var match  = false;
19663         var csel = this.view.getSelectedNodes();
19664         var cselitem = false;
19665         if (csel.length) {
19666             var ix = this.view.indexOf(csel[0]);
19667             cselitem  = this.store.getAt(ix);
19668             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19669                 cselitem = false;
19670             }
19671             
19672         }
19673         
19674         this.store.each(function(v) { 
19675             if (cselitem) {
19676                 // start at existing selection.
19677                 if (cselitem.id == v.id) {
19678                     cselitem = false;
19679                 }
19680                 return;
19681             }
19682                 
19683             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19684                 match = this.store.indexOf(v);
19685                 return false;
19686             }
19687         }, this);
19688         
19689         if (match === false) {
19690             return true; // no more action?
19691         }
19692         // scroll to?
19693         this.view.select(match);
19694         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19695         sn.scrollIntoView(sn.dom.parentNode, false);
19696     } 
19697
19698     /** 
19699     * @cfg {Boolean} grow 
19700     * @hide 
19701     */
19702     /** 
19703     * @cfg {Number} growMin 
19704     * @hide 
19705     */
19706     /** 
19707     * @cfg {Number} growMax 
19708     * @hide 
19709     */
19710     /**
19711      * @hide
19712      * @method autoSize
19713      */
19714 });/*
19715  * Copyright(c) 2010-2012, Roo J Solutions Limited
19716  *
19717  * Licence LGPL
19718  *
19719  */
19720
19721 /**
19722  * @class Roo.form.ComboBoxArray
19723  * @extends Roo.form.TextField
19724  * A facebook style adder... for lists of email / people / countries  etc...
19725  * pick multiple items from a combo box, and shows each one.
19726  *
19727  *  Fred [x]  Brian [x]  [Pick another |v]
19728  *
19729  *
19730  *  For this to work: it needs various extra information
19731  *    - normal combo problay has
19732  *      name, hiddenName
19733  *    + displayField, valueField
19734  *
19735  *    For our purpose...
19736  *
19737  *
19738  *   If we change from 'extends' to wrapping...
19739  *   
19740  *  
19741  *
19742  
19743  
19744  * @constructor
19745  * Create a new ComboBoxArray.
19746  * @param {Object} config Configuration options
19747  */
19748  
19749
19750 Roo.form.ComboBoxArray = function(config)
19751 {
19752     this.addEvents({
19753         /**
19754          * @event beforeremove
19755          * Fires before remove the value from the list
19756              * @param {Roo.form.ComboBoxArray} _self This combo box array
19757              * @param {Roo.form.ComboBoxArray.Item} item removed item
19758              */
19759         'beforeremove' : true,
19760         /**
19761          * @event remove
19762          * Fires when remove the value from the list
19763              * @param {Roo.form.ComboBoxArray} _self This combo box array
19764              * @param {Roo.form.ComboBoxArray.Item} item removed item
19765              */
19766         'remove' : true
19767         
19768         
19769     });
19770     
19771     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19772     
19773     this.items = new Roo.util.MixedCollection(false);
19774     
19775     // construct the child combo...
19776     
19777     
19778     
19779     
19780    
19781     
19782 }
19783
19784  
19785 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19786
19787     /**
19788      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19789      */
19790     
19791     lastData : false,
19792     
19793     // behavies liek a hiddne field
19794     inputType:      'hidden',
19795     /**
19796      * @cfg {Number} width The width of the box that displays the selected element
19797      */ 
19798     width:          300,
19799
19800     
19801     
19802     /**
19803      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19804      */
19805     name : false,
19806     /**
19807      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19808      */
19809     hiddenName : false,
19810     
19811     
19812     // private the array of items that are displayed..
19813     items  : false,
19814     // private - the hidden field el.
19815     hiddenEl : false,
19816     // private - the filed el..
19817     el : false,
19818     
19819     //validateValue : function() { return true; }, // all values are ok!
19820     //onAddClick: function() { },
19821     
19822     onRender : function(ct, position) 
19823     {
19824         
19825         // create the standard hidden element
19826         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19827         
19828         
19829         // give fake names to child combo;
19830         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19831         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19832         
19833         this.combo = Roo.factory(this.combo, Roo.form);
19834         this.combo.onRender(ct, position);
19835         if (typeof(this.combo.width) != 'undefined') {
19836             this.combo.onResize(this.combo.width,0);
19837         }
19838         
19839         this.combo.initEvents();
19840         
19841         // assigned so form know we need to do this..
19842         this.store          = this.combo.store;
19843         this.valueField     = this.combo.valueField;
19844         this.displayField   = this.combo.displayField ;
19845         
19846         
19847         this.combo.wrap.addClass('x-cbarray-grp');
19848         
19849         var cbwrap = this.combo.wrap.createChild(
19850             {tag: 'div', cls: 'x-cbarray-cb'},
19851             this.combo.el.dom
19852         );
19853         
19854              
19855         this.hiddenEl = this.combo.wrap.createChild({
19856             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19857         });
19858         this.el = this.combo.wrap.createChild({
19859             tag: 'input',  type:'hidden' , name: this.name, value : ''
19860         });
19861          //   this.el.dom.removeAttribute("name");
19862         
19863         
19864         this.outerWrap = this.combo.wrap;
19865         this.wrap = cbwrap;
19866         
19867         this.outerWrap.setWidth(this.width);
19868         this.outerWrap.dom.removeChild(this.el.dom);
19869         
19870         this.wrap.dom.appendChild(this.el.dom);
19871         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19872         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19873         
19874         this.combo.trigger.setStyle('position','relative');
19875         this.combo.trigger.setStyle('left', '0px');
19876         this.combo.trigger.setStyle('top', '2px');
19877         
19878         this.combo.el.setStyle('vertical-align', 'text-bottom');
19879         
19880         //this.trigger.setStyle('vertical-align', 'top');
19881         
19882         // this should use the code from combo really... on('add' ....)
19883         if (this.adder) {
19884             
19885         
19886             this.adder = this.outerWrap.createChild(
19887                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19888             var _t = this;
19889             this.adder.on('click', function(e) {
19890                 _t.fireEvent('adderclick', this, e);
19891             }, _t);
19892         }
19893         //var _t = this;
19894         //this.adder.on('click', this.onAddClick, _t);
19895         
19896         
19897         this.combo.on('select', function(cb, rec, ix) {
19898             this.addItem(rec.data);
19899             
19900             cb.setValue('');
19901             cb.el.dom.value = '';
19902             //cb.lastData = rec.data;
19903             // add to list
19904             
19905         }, this);
19906         
19907         
19908     },
19909     
19910     
19911     getName: function()
19912     {
19913         // returns hidden if it's set..
19914         if (!this.rendered) {return ''};
19915         return  this.hiddenName ? this.hiddenName : this.name;
19916         
19917     },
19918     
19919     
19920     onResize: function(w, h){
19921         
19922         return;
19923         // not sure if this is needed..
19924         //this.combo.onResize(w,h);
19925         
19926         if(typeof w != 'number'){
19927             // we do not handle it!?!?
19928             return;
19929         }
19930         var tw = this.combo.trigger.getWidth();
19931         tw += this.addicon ? this.addicon.getWidth() : 0;
19932         tw += this.editicon ? this.editicon.getWidth() : 0;
19933         var x = w - tw;
19934         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19935             
19936         this.combo.trigger.setStyle('left', '0px');
19937         
19938         if(this.list && this.listWidth === undefined){
19939             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19940             this.list.setWidth(lw);
19941             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19942         }
19943         
19944     
19945         
19946     },
19947     
19948     addItem: function(rec)
19949     {
19950         var valueField = this.combo.valueField;
19951         var displayField = this.combo.displayField;
19952         
19953         if (this.items.indexOfKey(rec[valueField]) > -1) {
19954             //console.log("GOT " + rec.data.id);
19955             return;
19956         }
19957         
19958         var x = new Roo.form.ComboBoxArray.Item({
19959             //id : rec[this.idField],
19960             data : rec,
19961             displayField : displayField ,
19962             tipField : displayField ,
19963             cb : this
19964         });
19965         // use the 
19966         this.items.add(rec[valueField],x);
19967         // add it before the element..
19968         this.updateHiddenEl();
19969         x.render(this.outerWrap, this.wrap.dom);
19970         // add the image handler..
19971     },
19972     
19973     updateHiddenEl : function()
19974     {
19975         this.validate();
19976         if (!this.hiddenEl) {
19977             return;
19978         }
19979         var ar = [];
19980         var idField = this.combo.valueField;
19981         
19982         this.items.each(function(f) {
19983             ar.push(f.data[idField]);
19984         });
19985         this.hiddenEl.dom.value = ar.join(',');
19986         this.validate();
19987     },
19988     
19989     reset : function()
19990     {
19991         this.items.clear();
19992         
19993         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19994            el.remove();
19995         });
19996         
19997         this.el.dom.value = '';
19998         if (this.hiddenEl) {
19999             this.hiddenEl.dom.value = '';
20000         }
20001         
20002     },
20003     getValue: function()
20004     {
20005         return this.hiddenEl ? this.hiddenEl.dom.value : '';
20006     },
20007     setValue: function(v) // not a valid action - must use addItems..
20008     {
20009         
20010         this.reset();
20011          
20012         if (this.store.isLocal && (typeof(v) == 'string')) {
20013             // then we can use the store to find the values..
20014             // comma seperated at present.. this needs to allow JSON based encoding..
20015             this.hiddenEl.value  = v;
20016             var v_ar = [];
20017             Roo.each(v.split(','), function(k) {
20018                 Roo.log("CHECK " + this.valueField + ',' + k);
20019                 var li = this.store.query(this.valueField, k);
20020                 if (!li.length) {
20021                     return;
20022                 }
20023                 var add = {};
20024                 add[this.valueField] = k;
20025                 add[this.displayField] = li.item(0).data[this.displayField];
20026                 
20027                 this.addItem(add);
20028             }, this) 
20029              
20030         }
20031         if (typeof(v) == 'object' ) {
20032             // then let's assume it's an array of objects..
20033             Roo.each(v, function(l) {
20034                 this.addItem(l);
20035             }, this);
20036              
20037         }
20038         
20039         
20040     },
20041     setFromData: function(v)
20042     {
20043         // this recieves an object, if setValues is called.
20044         this.reset();
20045         this.el.dom.value = v[this.displayField];
20046         this.hiddenEl.dom.value = v[this.valueField];
20047         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
20048             return;
20049         }
20050         var kv = v[this.valueField];
20051         var dv = v[this.displayField];
20052         kv = typeof(kv) != 'string' ? '' : kv;
20053         dv = typeof(dv) != 'string' ? '' : dv;
20054         
20055         
20056         var keys = kv.split(',');
20057         var display = dv.split(',');
20058         for (var i = 0 ; i < keys.length; i++) {
20059             
20060             add = {};
20061             add[this.valueField] = keys[i];
20062             add[this.displayField] = display[i];
20063             this.addItem(add);
20064         }
20065       
20066         
20067     },
20068     
20069     /**
20070      * Validates the combox array value
20071      * @return {Boolean} True if the value is valid, else false
20072      */
20073     validate : function(){
20074         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
20075             this.clearInvalid();
20076             return true;
20077         }
20078         return false;
20079     },
20080     
20081     validateValue : function(value){
20082         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
20083         
20084     },
20085     
20086     /*@
20087      * overide
20088      * 
20089      */
20090     isDirty : function() {
20091         if(this.disabled) {
20092             return false;
20093         }
20094         
20095         try {
20096             var d = Roo.decode(String(this.originalValue));
20097         } catch (e) {
20098             return String(this.getValue()) !== String(this.originalValue);
20099         }
20100         
20101         var originalValue = [];
20102         
20103         for (var i = 0; i < d.length; i++){
20104             originalValue.push(d[i][this.valueField]);
20105         }
20106         
20107         return String(this.getValue()) !== String(originalValue.join(','));
20108         
20109     }
20110     
20111 });
20112
20113
20114
20115 /**
20116  * @class Roo.form.ComboBoxArray.Item
20117  * @extends Roo.BoxComponent
20118  * A selected item in the list
20119  *  Fred [x]  Brian [x]  [Pick another |v]
20120  * 
20121  * @constructor
20122  * Create a new item.
20123  * @param {Object} config Configuration options
20124  */
20125  
20126 Roo.form.ComboBoxArray.Item = function(config) {
20127     config.id = Roo.id();
20128     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
20129 }
20130
20131 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
20132     data : {},
20133     cb: false,
20134     displayField : false,
20135     tipField : false,
20136     
20137     
20138     defaultAutoCreate : {
20139         tag: 'div',
20140         cls: 'x-cbarray-item',
20141         cn : [ 
20142             { tag: 'div' },
20143             {
20144                 tag: 'img',
20145                 width:16,
20146                 height : 16,
20147                 src : Roo.BLANK_IMAGE_URL ,
20148                 align: 'center'
20149             }
20150         ]
20151         
20152     },
20153     
20154  
20155     onRender : function(ct, position)
20156     {
20157         Roo.form.Field.superclass.onRender.call(this, ct, position);
20158         
20159         if(!this.el){
20160             var cfg = this.getAutoCreate();
20161             this.el = ct.createChild(cfg, position);
20162         }
20163         
20164         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
20165         
20166         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
20167             this.cb.renderer(this.data) :
20168             String.format('{0}',this.data[this.displayField]);
20169         
20170             
20171         this.el.child('div').dom.setAttribute('qtip',
20172                         String.format('{0}',this.data[this.tipField])
20173         );
20174         
20175         this.el.child('img').on('click', this.remove, this);
20176         
20177     },
20178    
20179     remove : function()
20180     {
20181         if(this.cb.disabled){
20182             return;
20183         }
20184         
20185         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
20186             this.cb.items.remove(this);
20187             this.el.child('img').un('click', this.remove, this);
20188             this.el.remove();
20189             this.cb.updateHiddenEl();
20190
20191             this.cb.fireEvent('remove', this.cb, this);
20192         }
20193         
20194     }
20195 });/*
20196  * RooJS Library 1.1.1
20197  * Copyright(c) 2008-2011  Alan Knowles
20198  *
20199  * License - LGPL
20200  */
20201  
20202
20203 /**
20204  * @class Roo.form.ComboNested
20205  * @extends Roo.form.ComboBox
20206  * A combobox for that allows selection of nested items in a list,
20207  * eg.
20208  *
20209  *  Book
20210  *    -> red
20211  *    -> green
20212  *  Table
20213  *    -> square
20214  *      ->red
20215  *      ->green
20216  *    -> rectangle
20217  *      ->green
20218  *      
20219  * 
20220  * @constructor
20221  * Create a new ComboNested
20222  * @param {Object} config Configuration options
20223  */
20224 Roo.form.ComboNested = function(config){
20225     Roo.form.ComboCheck.superclass.constructor.call(this, config);
20226     // should verify some data...
20227     // like
20228     // hiddenName = required..
20229     // displayField = required
20230     // valudField == required
20231     var req= [ 'hiddenName', 'displayField', 'valueField' ];
20232     var _t = this;
20233     Roo.each(req, function(e) {
20234         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
20235             throw "Roo.form.ComboNested : missing value for: " + e;
20236         }
20237     });
20238      
20239     
20240 };
20241
20242 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
20243    
20244     /*
20245      * @config {Number} max Number of columns to show
20246      */
20247     
20248     maxColumns : 3,
20249    
20250     list : null, // the outermost div..
20251     innerLists : null, // the
20252     views : null,
20253     stores : null,
20254     // private
20255     onRender : function(ct, position)
20256     {
20257         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
20258         
20259         if(this.hiddenName){
20260             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
20261                     'before', true);
20262             this.hiddenField.value =
20263                 this.hiddenValue !== undefined ? this.hiddenValue :
20264                 this.value !== undefined ? this.value : '';
20265
20266             // prevent input submission
20267             this.el.dom.removeAttribute('name');
20268              
20269              
20270         }
20271         
20272         if(Roo.isGecko){
20273             this.el.dom.setAttribute('autocomplete', 'off');
20274         }
20275
20276         var cls = 'x-combo-list';
20277
20278         this.list = new Roo.Layer({
20279             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
20280         });
20281
20282         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
20283         this.list.setWidth(lw);
20284         this.list.swallowEvent('mousewheel');
20285         this.assetHeight = 0;
20286
20287         if(this.title){
20288             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
20289             this.assetHeight += this.header.getHeight();
20290         }
20291         this.innerLists = [];
20292         this.views = [];
20293         this.stores = [];
20294         for (var i =0 ; i < this.maxColumns; i++) {
20295             this.onRenderList( cls, i);
20296         }
20297         
20298         // always needs footer, as we are going to have an 'OK' button.
20299         this.footer = this.list.createChild({cls:cls+'-ft'});
20300         this.pageTb = new Roo.Toolbar(this.footer);  
20301         var _this = this;
20302         this.pageTb.add(  {
20303             
20304             text: 'Done',
20305             handler: function()
20306             {
20307                 _this.collapse();
20308             }
20309         });
20310         
20311         if ( this.allowBlank && !this.disableClear) {
20312             
20313             this.pageTb.add(new Roo.Toolbar.Fill(), {
20314                 cls: 'x-btn-icon x-btn-clear',
20315                 text: '&#160;',
20316                 handler: function()
20317                 {
20318                     _this.collapse();
20319                     _this.clearValue();
20320                     _this.onSelect(false, -1);
20321                 }
20322             });
20323         }
20324         if (this.footer) {
20325             this.assetHeight += this.footer.getHeight();
20326         }
20327         
20328     },
20329     onRenderList : function (  cls, i)
20330     {
20331         
20332         var lw = Math.floor(
20333                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20334         );
20335         
20336         this.list.setWidth(lw); // default to '1'
20337
20338         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
20339         //il.on('mouseover', this.onViewOver, this, { list:  i });
20340         //il.on('mousemove', this.onViewMove, this, { list:  i });
20341         il.setWidth(lw);
20342         il.setStyle({ 'overflow-x' : 'hidden'});
20343
20344         if(!this.tpl){
20345             this.tpl = new Roo.Template({
20346                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
20347                 isEmpty: function (value, allValues) {
20348                     //Roo.log(value);
20349                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
20350                     return dl ? 'has-children' : 'no-children'
20351                 }
20352             });
20353         }
20354         
20355         var store  = this.store;
20356         if (i > 0) {
20357             store  = new Roo.data.SimpleStore({
20358                 //fields : this.store.reader.meta.fields,
20359                 reader : this.store.reader,
20360                 data : [ ]
20361             });
20362         }
20363         this.stores[i]  = store;
20364                 
20365         
20366         
20367         var view = this.views[i] = new Roo.View(
20368             il,
20369             this.tpl,
20370             {
20371                 singleSelect:true,
20372                 store: store,
20373                 selectedClass: this.selectedClass
20374             }
20375         );
20376         view.getEl().setWidth(lw);
20377         view.getEl().setStyle({
20378             position: i < 1 ? 'relative' : 'absolute',
20379             top: 0,
20380             left: (i * lw ) + 'px',
20381             display : i > 0 ? 'none' : 'block'
20382         });
20383         view.on('selectionchange', this.onSelectChange, this, {list : i });
20384         view.on('dblclick', this.onDoubleClick, this, {list : i });
20385         //view.on('click', this.onViewClick, this, { list : i });
20386
20387         store.on('beforeload', this.onBeforeLoad, this);
20388         store.on('load',  this.onLoad, this, { list  : i});
20389         store.on('loadexception', this.onLoadException, this);
20390
20391         // hide the other vies..
20392         
20393         
20394         
20395     },
20396     onResize : function()  {},
20397     
20398     restrictHeight : function()
20399     {
20400         var mh = 0;
20401         Roo.each(this.innerLists, function(il,i) {
20402             var el = this.views[i].getEl();
20403             el.dom.style.height = '';
20404             var inner = el.dom;
20405             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
20406             // only adjust heights on other ones..
20407             if (i < 1) {
20408                 
20409                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20410                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
20411                 mh = Math.max(el.getHeight(), mh);
20412             }
20413             
20414             
20415         }, this);
20416         
20417         this.list.beginUpdate();
20418         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20419         this.list.alignTo(this.el, this.listAlign);
20420         this.list.endUpdate();
20421         
20422     },
20423      
20424     
20425     // -- store handlers..
20426     // private
20427     onBeforeLoad : function()
20428     {
20429         if(!this.hasFocus){
20430             return;
20431         }
20432         this.innerLists[0].update(this.loadingText ?
20433                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20434         this.restrictHeight();
20435         this.selectedIndex = -1;
20436     },
20437     // private
20438     onLoad : function(a,b,c,d)
20439     {
20440         
20441         if(!this.hasFocus){
20442             return;
20443         }
20444         
20445         if(this.store.getCount() > 0) {
20446             this.expand();
20447             this.restrictHeight();   
20448         } else {
20449             this.onEmptyResults();
20450         }
20451         /*
20452         this.stores[1].loadData([]);
20453         this.stores[2].loadData([]);
20454         this.views
20455         */    
20456     
20457         //this.el.focus();
20458     },
20459     
20460     
20461     // private
20462     onLoadException : function()
20463     {
20464         this.collapse();
20465         Roo.log(this.store.reader.jsonData);
20466         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20467             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20468         }
20469         
20470         
20471     } ,
20472      
20473      
20474
20475     onSelectChange : function (view, sels, opts )
20476     {
20477         var ix = view.getSelectedIndexes();
20478         
20479         
20480         if (opts.list > this.maxColumns - 2) {
20481              
20482             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
20483             return;
20484         }
20485         
20486         if (!ix.length) {
20487             this.setFromData({});
20488             var str = this.stores[opts.list+1];
20489             str.loadData( str.reader.readerType == 'json' ? { data : [] } : [] );
20490             return;
20491         }
20492         
20493         var rec = view.store.getAt(ix[0]);
20494         this.setFromData(rec.data);
20495         
20496         var lw = Math.floor(
20497                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20498         );
20499         var data =  typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
20500         var dl = typeof(data.data) != 'undefined' ? data.total : data.length; ///json is a nested response..
20501         this.stores[opts.list+1].loadData( data );
20502         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20503         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20504         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20505         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1))); 
20506     },
20507     onDoubleClick : function()
20508     {
20509         this.collapse(); //??
20510     },
20511     
20512      
20513     
20514     findRecord : function (prop,value)
20515     {
20516         return this.findRecordInStore(this.store, prop,value);
20517     },
20518     
20519      // private
20520     findRecordInStore : function(store, prop, value)
20521     {
20522         var cstore = new Roo.data.SimpleStore({
20523             //fields : this.store.reader.meta.fields, // we need array reader.. for
20524             reader : this.store.reader,
20525             data : [ ]
20526         });
20527         var _this = this;
20528         var record  = false;
20529         if(store.getCount() > 0){
20530            store.each(function(r){
20531                 if(r.data[prop] == value){
20532                     record = r;
20533                     return false;
20534                 }
20535                 if (r.data.cn && r.data.cn.length) {
20536                     cstore.loadData( r.data.cn);
20537                     var cret = _this.findRecordInStore(cstore, prop, value);
20538                     if (cret !== false) {
20539                         record = cret;
20540                         return false;
20541                     }
20542                 }
20543                 
20544                 return true;
20545             });
20546         }
20547         return record;
20548     }
20549     
20550     
20551     
20552     
20553 });/*
20554  * Based on:
20555  * Ext JS Library 1.1.1
20556  * Copyright(c) 2006-2007, Ext JS, LLC.
20557  *
20558  * Originally Released Under LGPL - original licence link has changed is not relivant.
20559  *
20560  * Fork - LGPL
20561  * <script type="text/javascript">
20562  */
20563 /**
20564  * @class Roo.form.Checkbox
20565  * @extends Roo.form.Field
20566  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20567  * @constructor
20568  * Creates a new Checkbox
20569  * @param {Object} config Configuration options
20570  */
20571 Roo.form.Checkbox = function(config){
20572     Roo.form.Checkbox.superclass.constructor.call(this, config);
20573     this.addEvents({
20574         /**
20575          * @event check
20576          * Fires when the checkbox is checked or unchecked.
20577              * @param {Roo.form.Checkbox} this This checkbox
20578              * @param {Boolean} checked The new checked value
20579              */
20580         check : true
20581     });
20582 };
20583
20584 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20585     /**
20586      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20587      */
20588     focusClass : undefined,
20589     /**
20590      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20591      */
20592     fieldClass: "x-form-field",
20593     /**
20594      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20595      */
20596     checked: false,
20597     /**
20598      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20599      * {tag: "input", type: "checkbox", autocomplete: "off"})
20600      */
20601     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20602     /**
20603      * @cfg {String} boxLabel The text that appears beside the checkbox
20604      */
20605     boxLabel : "",
20606     /**
20607      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20608      */  
20609     inputValue : '1',
20610     /**
20611      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20612      */
20613      valueOff: '0', // value when not checked..
20614
20615     actionMode : 'viewEl', 
20616     //
20617     // private
20618     itemCls : 'x-menu-check-item x-form-item',
20619     groupClass : 'x-menu-group-item',
20620     inputType : 'hidden',
20621     
20622     
20623     inSetChecked: false, // check that we are not calling self...
20624     
20625     inputElement: false, // real input element?
20626     basedOn: false, // ????
20627     
20628     isFormField: true, // not sure where this is needed!!!!
20629
20630     onResize : function(){
20631         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20632         if(!this.boxLabel){
20633             this.el.alignTo(this.wrap, 'c-c');
20634         }
20635     },
20636
20637     initEvents : function(){
20638         Roo.form.Checkbox.superclass.initEvents.call(this);
20639         this.el.on("click", this.onClick,  this);
20640         this.el.on("change", this.onClick,  this);
20641     },
20642
20643
20644     getResizeEl : function(){
20645         return this.wrap;
20646     },
20647
20648     getPositionEl : function(){
20649         return this.wrap;
20650     },
20651
20652     // private
20653     onRender : function(ct, position){
20654         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20655         /*
20656         if(this.inputValue !== undefined){
20657             this.el.dom.value = this.inputValue;
20658         }
20659         */
20660         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20661         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20662         var viewEl = this.wrap.createChild({ 
20663             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20664         this.viewEl = viewEl;   
20665         this.wrap.on('click', this.onClick,  this); 
20666         
20667         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20668         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20669         
20670         
20671         
20672         if(this.boxLabel){
20673             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20674         //    viewEl.on('click', this.onClick,  this); 
20675         }
20676         //if(this.checked){
20677             this.setChecked(this.checked);
20678         //}else{
20679             //this.checked = this.el.dom;
20680         //}
20681
20682     },
20683
20684     // private
20685     initValue : Roo.emptyFn,
20686
20687     /**
20688      * Returns the checked state of the checkbox.
20689      * @return {Boolean} True if checked, else false
20690      */
20691     getValue : function(){
20692         if(this.el){
20693             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20694         }
20695         return this.valueOff;
20696         
20697     },
20698
20699         // private
20700     onClick : function(){ 
20701         if (this.disabled) {
20702             return;
20703         }
20704         this.setChecked(!this.checked);
20705
20706         //if(this.el.dom.checked != this.checked){
20707         //    this.setValue(this.el.dom.checked);
20708        // }
20709     },
20710
20711     /**
20712      * Sets the checked state of the checkbox.
20713      * On is always based on a string comparison between inputValue and the param.
20714      * @param {Boolean/String} value - the value to set 
20715      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20716      */
20717     setValue : function(v,suppressEvent){
20718         
20719         
20720         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20721         //if(this.el && this.el.dom){
20722         //    this.el.dom.checked = this.checked;
20723         //    this.el.dom.defaultChecked = this.checked;
20724         //}
20725         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20726         //this.fireEvent("check", this, this.checked);
20727     },
20728     // private..
20729     setChecked : function(state,suppressEvent)
20730     {
20731         if (this.inSetChecked) {
20732             this.checked = state;
20733             return;
20734         }
20735         
20736     
20737         if(this.wrap){
20738             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20739         }
20740         this.checked = state;
20741         if(suppressEvent !== true){
20742             this.fireEvent('check', this, state);
20743         }
20744         this.inSetChecked = true;
20745         this.el.dom.value = state ? this.inputValue : this.valueOff;
20746         this.inSetChecked = false;
20747         
20748     },
20749     // handle setting of hidden value by some other method!!?!?
20750     setFromHidden: function()
20751     {
20752         if(!this.el){
20753             return;
20754         }
20755         //console.log("SET FROM HIDDEN");
20756         //alert('setFrom hidden');
20757         this.setValue(this.el.dom.value);
20758     },
20759     
20760     onDestroy : function()
20761     {
20762         if(this.viewEl){
20763             Roo.get(this.viewEl).remove();
20764         }
20765          
20766         Roo.form.Checkbox.superclass.onDestroy.call(this);
20767     },
20768     
20769     setBoxLabel : function(str)
20770     {
20771         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20772     }
20773
20774 });/*
20775  * Based on:
20776  * Ext JS Library 1.1.1
20777  * Copyright(c) 2006-2007, Ext JS, LLC.
20778  *
20779  * Originally Released Under LGPL - original licence link has changed is not relivant.
20780  *
20781  * Fork - LGPL
20782  * <script type="text/javascript">
20783  */
20784  
20785 /**
20786  * @class Roo.form.Radio
20787  * @extends Roo.form.Checkbox
20788  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20789  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20790  * @constructor
20791  * Creates a new Radio
20792  * @param {Object} config Configuration options
20793  */
20794 Roo.form.Radio = function(){
20795     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20796 };
20797 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20798     inputType: 'radio',
20799
20800     /**
20801      * If this radio is part of a group, it will return the selected value
20802      * @return {String}
20803      */
20804     getGroupValue : function(){
20805         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20806     },
20807     
20808     
20809     onRender : function(ct, position){
20810         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20811         
20812         if(this.inputValue !== undefined){
20813             this.el.dom.value = this.inputValue;
20814         }
20815          
20816         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20817         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20818         //var viewEl = this.wrap.createChild({ 
20819         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20820         //this.viewEl = viewEl;   
20821         //this.wrap.on('click', this.onClick,  this); 
20822         
20823         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20824         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20825         
20826         
20827         
20828         if(this.boxLabel){
20829             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20830         //    viewEl.on('click', this.onClick,  this); 
20831         }
20832          if(this.checked){
20833             this.el.dom.checked =   'checked' ;
20834         }
20835          
20836     } 
20837     
20838     
20839 });//<script type="text/javascript">
20840
20841 /*
20842  * Based  Ext JS Library 1.1.1
20843  * Copyright(c) 2006-2007, Ext JS, LLC.
20844  * LGPL
20845  *
20846  */
20847  
20848 /**
20849  * @class Roo.HtmlEditorCore
20850  * @extends Roo.Component
20851  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20852  *
20853  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20854  */
20855
20856 Roo.HtmlEditorCore = function(config){
20857     
20858     
20859     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20860     
20861     
20862     this.addEvents({
20863         /**
20864          * @event initialize
20865          * Fires when the editor is fully initialized (including the iframe)
20866          * @param {Roo.HtmlEditorCore} this
20867          */
20868         initialize: true,
20869         /**
20870          * @event activate
20871          * Fires when the editor is first receives the focus. Any insertion must wait
20872          * until after this event.
20873          * @param {Roo.HtmlEditorCore} this
20874          */
20875         activate: true,
20876          /**
20877          * @event beforesync
20878          * Fires before the textarea is updated with content from the editor iframe. Return false
20879          * to cancel the sync.
20880          * @param {Roo.HtmlEditorCore} this
20881          * @param {String} html
20882          */
20883         beforesync: true,
20884          /**
20885          * @event beforepush
20886          * Fires before the iframe editor is updated with content from the textarea. Return false
20887          * to cancel the push.
20888          * @param {Roo.HtmlEditorCore} this
20889          * @param {String} html
20890          */
20891         beforepush: true,
20892          /**
20893          * @event sync
20894          * Fires when the textarea is updated with content from the editor iframe.
20895          * @param {Roo.HtmlEditorCore} this
20896          * @param {String} html
20897          */
20898         sync: true,
20899          /**
20900          * @event push
20901          * Fires when the iframe editor is updated with content from the textarea.
20902          * @param {Roo.HtmlEditorCore} this
20903          * @param {String} html
20904          */
20905         push: true,
20906         
20907         /**
20908          * @event editorevent
20909          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20910          * @param {Roo.HtmlEditorCore} this
20911          */
20912         editorevent: true
20913         
20914     });
20915     
20916     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20917     
20918     // defaults : white / black...
20919     this.applyBlacklists();
20920     
20921     
20922     
20923 };
20924
20925
20926 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20927
20928
20929      /**
20930      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20931      */
20932     
20933     owner : false,
20934     
20935      /**
20936      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20937      *                        Roo.resizable.
20938      */
20939     resizable : false,
20940      /**
20941      * @cfg {Number} height (in pixels)
20942      */   
20943     height: 300,
20944    /**
20945      * @cfg {Number} width (in pixels)
20946      */   
20947     width: 500,
20948     
20949     /**
20950      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20951      * 
20952      */
20953     stylesheets: false,
20954     
20955     // id of frame..
20956     frameId: false,
20957     
20958     // private properties
20959     validationEvent : false,
20960     deferHeight: true,
20961     initialized : false,
20962     activated : false,
20963     sourceEditMode : false,
20964     onFocus : Roo.emptyFn,
20965     iframePad:3,
20966     hideMode:'offsets',
20967     
20968     clearUp: true,
20969     
20970     // blacklist + whitelisted elements..
20971     black: false,
20972     white: false,
20973      
20974     bodyCls : '',
20975
20976     /**
20977      * Protected method that will not generally be called directly. It
20978      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20979      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20980      */
20981     getDocMarkup : function(){
20982         // body styles..
20983         var st = '';
20984         
20985         // inherit styels from page...?? 
20986         if (this.stylesheets === false) {
20987             
20988             Roo.get(document.head).select('style').each(function(node) {
20989                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20990             });
20991             
20992             Roo.get(document.head).select('link').each(function(node) { 
20993                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20994             });
20995             
20996         } else if (!this.stylesheets.length) {
20997                 // simple..
20998                 st = '<style type="text/css">' +
20999                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21000                    '</style>';
21001         } else { 
21002             st = '<style type="text/css">' +
21003                     this.stylesheets +
21004                 '</style>';
21005         }
21006         
21007         st +=  '<style type="text/css">' +
21008             'IMG { cursor: pointer } ' +
21009         '</style>';
21010
21011         var cls = 'roo-htmleditor-body';
21012         
21013         if(this.bodyCls.length){
21014             cls += ' ' + this.bodyCls;
21015         }
21016         
21017         return '<html><head>' + st  +
21018             //<style type="text/css">' +
21019             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21020             //'</style>' +
21021             ' </head><body class="' +  cls + '"></body></html>';
21022     },
21023
21024     // private
21025     onRender : function(ct, position)
21026     {
21027         var _t = this;
21028         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21029         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21030         
21031         
21032         this.el.dom.style.border = '0 none';
21033         this.el.dom.setAttribute('tabIndex', -1);
21034         this.el.addClass('x-hidden hide');
21035         
21036         
21037         
21038         if(Roo.isIE){ // fix IE 1px bogus margin
21039             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21040         }
21041        
21042         
21043         this.frameId = Roo.id();
21044         
21045          
21046         
21047         var iframe = this.owner.wrap.createChild({
21048             tag: 'iframe',
21049             cls: 'form-control', // bootstrap..
21050             id: this.frameId,
21051             name: this.frameId,
21052             frameBorder : 'no',
21053             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21054         }, this.el
21055         );
21056         
21057         
21058         this.iframe = iframe.dom;
21059
21060          this.assignDocWin();
21061         
21062         this.doc.designMode = 'on';
21063        
21064         this.doc.open();
21065         this.doc.write(this.getDocMarkup());
21066         this.doc.close();
21067
21068         
21069         var task = { // must defer to wait for browser to be ready
21070             run : function(){
21071                 //console.log("run task?" + this.doc.readyState);
21072                 this.assignDocWin();
21073                 if(this.doc.body || this.doc.readyState == 'complete'){
21074                     try {
21075                         this.doc.designMode="on";
21076                     } catch (e) {
21077                         return;
21078                     }
21079                     Roo.TaskMgr.stop(task);
21080                     this.initEditor.defer(10, this);
21081                 }
21082             },
21083             interval : 10,
21084             duration: 10000,
21085             scope: this
21086         };
21087         Roo.TaskMgr.start(task);
21088
21089     },
21090
21091     // private
21092     onResize : function(w, h)
21093     {
21094          Roo.log('resize: ' +w + ',' + h );
21095         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21096         if(!this.iframe){
21097             return;
21098         }
21099         if(typeof w == 'number'){
21100             
21101             this.iframe.style.width = w + 'px';
21102         }
21103         if(typeof h == 'number'){
21104             
21105             this.iframe.style.height = h + 'px';
21106             if(this.doc){
21107                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21108             }
21109         }
21110         
21111     },
21112
21113     /**
21114      * Toggles the editor between standard and source edit mode.
21115      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21116      */
21117     toggleSourceEdit : function(sourceEditMode){
21118         
21119         this.sourceEditMode = sourceEditMode === true;
21120         
21121         if(this.sourceEditMode){
21122  
21123             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21124             
21125         }else{
21126             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21127             //this.iframe.className = '';
21128             this.deferFocus();
21129         }
21130         //this.setSize(this.owner.wrap.getSize());
21131         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21132     },
21133
21134     
21135   
21136
21137     /**
21138      * Protected method that will not generally be called directly. If you need/want
21139      * custom HTML cleanup, this is the method you should override.
21140      * @param {String} html The HTML to be cleaned
21141      * return {String} The cleaned HTML
21142      */
21143     cleanHtml : function(html){
21144         html = String(html);
21145         if(html.length > 5){
21146             if(Roo.isSafari){ // strip safari nonsense
21147                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21148             }
21149         }
21150         if(html == '&nbsp;'){
21151             html = '';
21152         }
21153         return html;
21154     },
21155
21156     /**
21157      * HTML Editor -> Textarea
21158      * Protected method that will not generally be called directly. Syncs the contents
21159      * of the editor iframe with the textarea.
21160      */
21161     syncValue : function(){
21162         if(this.initialized){
21163             var bd = (this.doc.body || this.doc.documentElement);
21164             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21165             var html = bd.innerHTML;
21166             if(Roo.isSafari){
21167                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21168                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21169                 if(m && m[1]){
21170                     html = '<div style="'+m[0]+'">' + html + '</div>';
21171                 }
21172             }
21173             html = this.cleanHtml(html);
21174             // fix up the special chars.. normaly like back quotes in word...
21175             // however we do not want to do this with chinese..
21176             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
21177                 
21178                 var cc = match.charCodeAt();
21179
21180                 // Get the character value, handling surrogate pairs
21181                 if (match.length == 2) {
21182                     // It's a surrogate pair, calculate the Unicode code point
21183                     var high = match.charCodeAt(0) - 0xD800;
21184                     var low  = match.charCodeAt(1) - 0xDC00;
21185                     cc = (high * 0x400) + low + 0x10000;
21186                 }  else if (
21187                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21188                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21189                     (cc >= 0xf900 && cc < 0xfb00 )
21190                 ) {
21191                         return match;
21192                 }  
21193          
21194                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
21195                 return "&#" + cc + ";";
21196                 
21197                 
21198             });
21199             
21200             
21201              
21202             if(this.owner.fireEvent('beforesync', this, html) !== false){
21203                 this.el.dom.value = html;
21204                 this.owner.fireEvent('sync', this, html);
21205             }
21206         }
21207     },
21208
21209     /**
21210      * Protected method that will not generally be called directly. Pushes the value of the textarea
21211      * into the iframe editor.
21212      */
21213     pushValue : function(){
21214         if(this.initialized){
21215             var v = this.el.dom.value.trim();
21216             
21217 //            if(v.length < 1){
21218 //                v = '&#160;';
21219 //            }
21220             
21221             if(this.owner.fireEvent('beforepush', this, v) !== false){
21222                 var d = (this.doc.body || this.doc.documentElement);
21223                 d.innerHTML = v;
21224                 this.cleanUpPaste();
21225                 this.el.dom.value = d.innerHTML;
21226                 this.owner.fireEvent('push', this, v);
21227             }
21228         }
21229     },
21230
21231     // private
21232     deferFocus : function(){
21233         this.focus.defer(10, this);
21234     },
21235
21236     // doc'ed in Field
21237     focus : function(){
21238         if(this.win && !this.sourceEditMode){
21239             this.win.focus();
21240         }else{
21241             this.el.focus();
21242         }
21243     },
21244     
21245     assignDocWin: function()
21246     {
21247         var iframe = this.iframe;
21248         
21249          if(Roo.isIE){
21250             this.doc = iframe.contentWindow.document;
21251             this.win = iframe.contentWindow;
21252         } else {
21253 //            if (!Roo.get(this.frameId)) {
21254 //                return;
21255 //            }
21256 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21257 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21258             
21259             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21260                 return;
21261             }
21262             
21263             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21264             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21265         }
21266     },
21267     
21268     // private
21269     initEditor : function(){
21270         //console.log("INIT EDITOR");
21271         this.assignDocWin();
21272         
21273         
21274         
21275         this.doc.designMode="on";
21276         this.doc.open();
21277         this.doc.write(this.getDocMarkup());
21278         this.doc.close();
21279         
21280         var dbody = (this.doc.body || this.doc.documentElement);
21281         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21282         // this copies styles from the containing element into thsi one..
21283         // not sure why we need all of this..
21284         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21285         
21286         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21287         //ss['background-attachment'] = 'fixed'; // w3c
21288         dbody.bgProperties = 'fixed'; // ie
21289         //Roo.DomHelper.applyStyles(dbody, ss);
21290         Roo.EventManager.on(this.doc, {
21291             //'mousedown': this.onEditorEvent,
21292             'mouseup': this.onEditorEvent,
21293             'dblclick': this.onEditorEvent,
21294             'click': this.onEditorEvent,
21295             'keyup': this.onEditorEvent,
21296             buffer:100,
21297             scope: this
21298         });
21299         if(Roo.isGecko){
21300             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21301         }
21302         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21303             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21304         }
21305         this.initialized = true;
21306
21307         this.owner.fireEvent('initialize', this);
21308         this.pushValue();
21309     },
21310
21311     // private
21312     onDestroy : function(){
21313         
21314         
21315         
21316         if(this.rendered){
21317             
21318             //for (var i =0; i < this.toolbars.length;i++) {
21319             //    // fixme - ask toolbars for heights?
21320             //    this.toolbars[i].onDestroy();
21321            // }
21322             
21323             //this.wrap.dom.innerHTML = '';
21324             //this.wrap.remove();
21325         }
21326     },
21327
21328     // private
21329     onFirstFocus : function(){
21330         
21331         this.assignDocWin();
21332         
21333         
21334         this.activated = true;
21335          
21336     
21337         if(Roo.isGecko){ // prevent silly gecko errors
21338             this.win.focus();
21339             var s = this.win.getSelection();
21340             if(!s.focusNode || s.focusNode.nodeType != 3){
21341                 var r = s.getRangeAt(0);
21342                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21343                 r.collapse(true);
21344                 this.deferFocus();
21345             }
21346             try{
21347                 this.execCmd('useCSS', true);
21348                 this.execCmd('styleWithCSS', false);
21349             }catch(e){}
21350         }
21351         this.owner.fireEvent('activate', this);
21352     },
21353
21354     // private
21355     adjustFont: function(btn){
21356         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21357         //if(Roo.isSafari){ // safari
21358         //    adjust *= 2;
21359        // }
21360         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21361         if(Roo.isSafari){ // safari
21362             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21363             v =  (v < 10) ? 10 : v;
21364             v =  (v > 48) ? 48 : v;
21365             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21366             
21367         }
21368         
21369         
21370         v = Math.max(1, v+adjust);
21371         
21372         this.execCmd('FontSize', v  );
21373     },
21374
21375     onEditorEvent : function(e)
21376     {
21377         this.owner.fireEvent('editorevent', this, e);
21378       //  this.updateToolbar();
21379         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21380     },
21381
21382     insertTag : function(tg)
21383     {
21384         // could be a bit smarter... -> wrap the current selected tRoo..
21385         if (tg.toLowerCase() == 'span' ||
21386             tg.toLowerCase() == 'code' ||
21387             tg.toLowerCase() == 'sup' ||
21388             tg.toLowerCase() == 'sub' 
21389             ) {
21390             
21391             range = this.createRange(this.getSelection());
21392             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21393             wrappingNode.appendChild(range.extractContents());
21394             range.insertNode(wrappingNode);
21395
21396             return;
21397             
21398             
21399             
21400         }
21401         this.execCmd("formatblock",   tg);
21402         
21403     },
21404     
21405     insertText : function(txt)
21406     {
21407         
21408         
21409         var range = this.createRange();
21410         range.deleteContents();
21411                //alert(Sender.getAttribute('label'));
21412                
21413         range.insertNode(this.doc.createTextNode(txt));
21414     } ,
21415     
21416      
21417
21418     /**
21419      * Executes a Midas editor command on the editor document and performs necessary focus and
21420      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21421      * @param {String} cmd The Midas command
21422      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21423      */
21424     relayCmd : function(cmd, value){
21425         this.win.focus();
21426         this.execCmd(cmd, value);
21427         this.owner.fireEvent('editorevent', this);
21428         //this.updateToolbar();
21429         this.owner.deferFocus();
21430     },
21431
21432     /**
21433      * Executes a Midas editor command directly on the editor document.
21434      * For visual commands, you should use {@link #relayCmd} instead.
21435      * <b>This should only be called after the editor is initialized.</b>
21436      * @param {String} cmd The Midas command
21437      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21438      */
21439     execCmd : function(cmd, value){
21440         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21441         this.syncValue();
21442     },
21443  
21444  
21445    
21446     /**
21447      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21448      * to insert tRoo.
21449      * @param {String} text | dom node.. 
21450      */
21451     insertAtCursor : function(text)
21452     {
21453         
21454         if(!this.activated){
21455             return;
21456         }
21457         /*
21458         if(Roo.isIE){
21459             this.win.focus();
21460             var r = this.doc.selection.createRange();
21461             if(r){
21462                 r.collapse(true);
21463                 r.pasteHTML(text);
21464                 this.syncValue();
21465                 this.deferFocus();
21466             
21467             }
21468             return;
21469         }
21470         */
21471         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21472             this.win.focus();
21473             
21474             
21475             // from jquery ui (MIT licenced)
21476             var range, node;
21477             var win = this.win;
21478             
21479             if (win.getSelection && win.getSelection().getRangeAt) {
21480                 range = win.getSelection().getRangeAt(0);
21481                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21482                 range.insertNode(node);
21483             } else if (win.document.selection && win.document.selection.createRange) {
21484                 // no firefox support
21485                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21486                 win.document.selection.createRange().pasteHTML(txt);
21487             } else {
21488                 // no firefox support
21489                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21490                 this.execCmd('InsertHTML', txt);
21491             } 
21492             
21493             this.syncValue();
21494             
21495             this.deferFocus();
21496         }
21497     },
21498  // private
21499     mozKeyPress : function(e){
21500         if(e.ctrlKey){
21501             var c = e.getCharCode(), cmd;
21502           
21503             if(c > 0){
21504                 c = String.fromCharCode(c).toLowerCase();
21505                 switch(c){
21506                     case 'b':
21507                         cmd = 'bold';
21508                         break;
21509                     case 'i':
21510                         cmd = 'italic';
21511                         break;
21512                     
21513                     case 'u':
21514                         cmd = 'underline';
21515                         break;
21516                     
21517                     case 'v':
21518                         this.cleanUpPaste.defer(100, this);
21519                         return;
21520                         
21521                 }
21522                 if(cmd){
21523                     this.win.focus();
21524                     this.execCmd(cmd);
21525                     this.deferFocus();
21526                     e.preventDefault();
21527                 }
21528                 
21529             }
21530         }
21531     },
21532
21533     // private
21534     fixKeys : function(){ // load time branching for fastest keydown performance
21535         if(Roo.isIE){
21536             return function(e){
21537                 var k = e.getKey(), r;
21538                 if(k == e.TAB){
21539                     e.stopEvent();
21540                     r = this.doc.selection.createRange();
21541                     if(r){
21542                         r.collapse(true);
21543                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21544                         this.deferFocus();
21545                     }
21546                     return;
21547                 }
21548                 
21549                 if(k == e.ENTER){
21550                     r = this.doc.selection.createRange();
21551                     if(r){
21552                         var target = r.parentElement();
21553                         if(!target || target.tagName.toLowerCase() != 'li'){
21554                             e.stopEvent();
21555                             r.pasteHTML('<br />');
21556                             r.collapse(false);
21557                             r.select();
21558                         }
21559                     }
21560                 }
21561                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21562                     this.cleanUpPaste.defer(100, this);
21563                     return;
21564                 }
21565                 
21566                 
21567             };
21568         }else if(Roo.isOpera){
21569             return function(e){
21570                 var k = e.getKey();
21571                 if(k == e.TAB){
21572                     e.stopEvent();
21573                     this.win.focus();
21574                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21575                     this.deferFocus();
21576                 }
21577                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21578                     this.cleanUpPaste.defer(100, this);
21579                     return;
21580                 }
21581                 
21582             };
21583         }else if(Roo.isSafari){
21584             return function(e){
21585                 var k = e.getKey();
21586                 
21587                 if(k == e.TAB){
21588                     e.stopEvent();
21589                     this.execCmd('InsertText','\t');
21590                     this.deferFocus();
21591                     return;
21592                 }
21593                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21594                     this.cleanUpPaste.defer(100, this);
21595                     return;
21596                 }
21597                 
21598              };
21599         }
21600     }(),
21601     
21602     getAllAncestors: function()
21603     {
21604         var p = this.getSelectedNode();
21605         var a = [];
21606         if (!p) {
21607             a.push(p); // push blank onto stack..
21608             p = this.getParentElement();
21609         }
21610         
21611         
21612         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21613             a.push(p);
21614             p = p.parentNode;
21615         }
21616         a.push(this.doc.body);
21617         return a;
21618     },
21619     lastSel : false,
21620     lastSelNode : false,
21621     
21622     
21623     getSelection : function() 
21624     {
21625         this.assignDocWin();
21626         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21627     },
21628     
21629     getSelectedNode: function() 
21630     {
21631         // this may only work on Gecko!!!
21632         
21633         // should we cache this!!!!
21634         
21635         
21636         
21637          
21638         var range = this.createRange(this.getSelection()).cloneRange();
21639         
21640         if (Roo.isIE) {
21641             var parent = range.parentElement();
21642             while (true) {
21643                 var testRange = range.duplicate();
21644                 testRange.moveToElementText(parent);
21645                 if (testRange.inRange(range)) {
21646                     break;
21647                 }
21648                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21649                     break;
21650                 }
21651                 parent = parent.parentElement;
21652             }
21653             return parent;
21654         }
21655         
21656         // is ancestor a text element.
21657         var ac =  range.commonAncestorContainer;
21658         if (ac.nodeType == 3) {
21659             ac = ac.parentNode;
21660         }
21661         
21662         var ar = ac.childNodes;
21663          
21664         var nodes = [];
21665         var other_nodes = [];
21666         var has_other_nodes = false;
21667         for (var i=0;i<ar.length;i++) {
21668             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21669                 continue;
21670             }
21671             // fullly contained node.
21672             
21673             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21674                 nodes.push(ar[i]);
21675                 continue;
21676             }
21677             
21678             // probably selected..
21679             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21680                 other_nodes.push(ar[i]);
21681                 continue;
21682             }
21683             // outer..
21684             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21685                 continue;
21686             }
21687             
21688             
21689             has_other_nodes = true;
21690         }
21691         if (!nodes.length && other_nodes.length) {
21692             nodes= other_nodes;
21693         }
21694         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21695             return false;
21696         }
21697         
21698         return nodes[0];
21699     },
21700     createRange: function(sel)
21701     {
21702         // this has strange effects when using with 
21703         // top toolbar - not sure if it's a great idea.
21704         //this.editor.contentWindow.focus();
21705         if (typeof sel != "undefined") {
21706             try {
21707                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21708             } catch(e) {
21709                 return this.doc.createRange();
21710             }
21711         } else {
21712             return this.doc.createRange();
21713         }
21714     },
21715     getParentElement: function()
21716     {
21717         
21718         this.assignDocWin();
21719         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21720         
21721         var range = this.createRange(sel);
21722          
21723         try {
21724             var p = range.commonAncestorContainer;
21725             while (p.nodeType == 3) { // text node
21726                 p = p.parentNode;
21727             }
21728             return p;
21729         } catch (e) {
21730             return null;
21731         }
21732     
21733     },
21734     /***
21735      *
21736      * Range intersection.. the hard stuff...
21737      *  '-1' = before
21738      *  '0' = hits..
21739      *  '1' = after.
21740      *         [ -- selected range --- ]
21741      *   [fail]                        [fail]
21742      *
21743      *    basically..
21744      *      if end is before start or  hits it. fail.
21745      *      if start is after end or hits it fail.
21746      *
21747      *   if either hits (but other is outside. - then it's not 
21748      *   
21749      *    
21750      **/
21751     
21752     
21753     // @see http://www.thismuchiknow.co.uk/?p=64.
21754     rangeIntersectsNode : function(range, node)
21755     {
21756         var nodeRange = node.ownerDocument.createRange();
21757         try {
21758             nodeRange.selectNode(node);
21759         } catch (e) {
21760             nodeRange.selectNodeContents(node);
21761         }
21762     
21763         var rangeStartRange = range.cloneRange();
21764         rangeStartRange.collapse(true);
21765     
21766         var rangeEndRange = range.cloneRange();
21767         rangeEndRange.collapse(false);
21768     
21769         var nodeStartRange = nodeRange.cloneRange();
21770         nodeStartRange.collapse(true);
21771     
21772         var nodeEndRange = nodeRange.cloneRange();
21773         nodeEndRange.collapse(false);
21774     
21775         return rangeStartRange.compareBoundaryPoints(
21776                  Range.START_TO_START, nodeEndRange) == -1 &&
21777                rangeEndRange.compareBoundaryPoints(
21778                  Range.START_TO_START, nodeStartRange) == 1;
21779         
21780          
21781     },
21782     rangeCompareNode : function(range, node)
21783     {
21784         var nodeRange = node.ownerDocument.createRange();
21785         try {
21786             nodeRange.selectNode(node);
21787         } catch (e) {
21788             nodeRange.selectNodeContents(node);
21789         }
21790         
21791         
21792         range.collapse(true);
21793     
21794         nodeRange.collapse(true);
21795      
21796         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21797         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21798          
21799         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21800         
21801         var nodeIsBefore   =  ss == 1;
21802         var nodeIsAfter    = ee == -1;
21803         
21804         if (nodeIsBefore && nodeIsAfter) {
21805             return 0; // outer
21806         }
21807         if (!nodeIsBefore && nodeIsAfter) {
21808             return 1; //right trailed.
21809         }
21810         
21811         if (nodeIsBefore && !nodeIsAfter) {
21812             return 2;  // left trailed.
21813         }
21814         // fully contined.
21815         return 3;
21816     },
21817
21818     // private? - in a new class?
21819     cleanUpPaste :  function()
21820     {
21821         // cleans up the whole document..
21822         Roo.log('cleanuppaste');
21823         
21824         this.cleanUpChildren(this.doc.body);
21825         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21826         if (clean != this.doc.body.innerHTML) {
21827             this.doc.body.innerHTML = clean;
21828         }
21829         
21830     },
21831     
21832     cleanWordChars : function(input) {// change the chars to hex code
21833         var he = Roo.HtmlEditorCore;
21834         
21835         var output = input;
21836         Roo.each(he.swapCodes, function(sw) { 
21837             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21838             
21839             output = output.replace(swapper, sw[1]);
21840         });
21841         
21842         return output;
21843     },
21844     
21845     
21846     cleanUpChildren : function (n)
21847     {
21848         if (!n.childNodes.length) {
21849             return;
21850         }
21851         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21852            this.cleanUpChild(n.childNodes[i]);
21853         }
21854     },
21855     
21856     
21857         
21858     
21859     cleanUpChild : function (node)
21860     {
21861         var ed = this;
21862         //console.log(node);
21863         if (node.nodeName == "#text") {
21864             // clean up silly Windows -- stuff?
21865             return; 
21866         }
21867         if (node.nodeName == "#comment") {
21868             node.parentNode.removeChild(node);
21869             // clean up silly Windows -- stuff?
21870             return; 
21871         }
21872         var lcname = node.tagName.toLowerCase();
21873         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21874         // whitelist of tags..
21875         
21876         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21877             // remove node.
21878             node.parentNode.removeChild(node);
21879             return;
21880             
21881         }
21882         
21883         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21884         
21885         // spans with no attributes - just remove them..
21886         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21887             remove_keep_children = true;
21888         }
21889         
21890         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21891         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21892         
21893         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21894         //    remove_keep_children = true;
21895         //}
21896         
21897         if (remove_keep_children) {
21898             this.cleanUpChildren(node);
21899             // inserts everything just before this node...
21900             while (node.childNodes.length) {
21901                 var cn = node.childNodes[0];
21902                 node.removeChild(cn);
21903                 node.parentNode.insertBefore(cn, node);
21904             }
21905             node.parentNode.removeChild(node);
21906             return;
21907         }
21908         
21909         if (!node.attributes || !node.attributes.length) {
21910             
21911           
21912             
21913             
21914             this.cleanUpChildren(node);
21915             return;
21916         }
21917         
21918         function cleanAttr(n,v)
21919         {
21920             
21921             if (v.match(/^\./) || v.match(/^\//)) {
21922                 return;
21923             }
21924             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21925                 return;
21926             }
21927             if (v.match(/^#/)) {
21928                 return;
21929             }
21930 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21931             node.removeAttribute(n);
21932             
21933         }
21934         
21935         var cwhite = this.cwhite;
21936         var cblack = this.cblack;
21937             
21938         function cleanStyle(n,v)
21939         {
21940             if (v.match(/expression/)) { //XSS?? should we even bother..
21941                 node.removeAttribute(n);
21942                 return;
21943             }
21944             
21945             var parts = v.split(/;/);
21946             var clean = [];
21947             
21948             Roo.each(parts, function(p) {
21949                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21950                 if (!p.length) {
21951                     return true;
21952                 }
21953                 var l = p.split(':').shift().replace(/\s+/g,'');
21954                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21955                 
21956                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21957 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21958                     //node.removeAttribute(n);
21959                     return true;
21960                 }
21961                 //Roo.log()
21962                 // only allow 'c whitelisted system attributes'
21963                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21964 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21965                     //node.removeAttribute(n);
21966                     return true;
21967                 }
21968                 
21969                 
21970                  
21971                 
21972                 clean.push(p);
21973                 return true;
21974             });
21975             if (clean.length) { 
21976                 node.setAttribute(n, clean.join(';'));
21977             } else {
21978                 node.removeAttribute(n);
21979             }
21980             
21981         }
21982         
21983         
21984         for (var i = node.attributes.length-1; i > -1 ; i--) {
21985             var a = node.attributes[i];
21986             //console.log(a);
21987             
21988             if (a.name.toLowerCase().substr(0,2)=='on')  {
21989                 node.removeAttribute(a.name);
21990                 continue;
21991             }
21992             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21993                 node.removeAttribute(a.name);
21994                 continue;
21995             }
21996             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21997                 cleanAttr(a.name,a.value); // fixme..
21998                 continue;
21999             }
22000             if (a.name == 'style') {
22001                 cleanStyle(a.name,a.value);
22002                 continue;
22003             }
22004             /// clean up MS crap..
22005             // tecnically this should be a list of valid class'es..
22006             
22007             
22008             if (a.name == 'class') {
22009                 if (a.value.match(/^Mso/)) {
22010                     node.removeAttribute('class');
22011                 }
22012                 
22013                 if (a.value.match(/^body$/)) {
22014                     node.removeAttribute('class');
22015                 }
22016                 continue;
22017             }
22018             
22019             // style cleanup!?
22020             // class cleanup?
22021             
22022         }
22023         
22024         
22025         this.cleanUpChildren(node);
22026         
22027         
22028     },
22029     
22030     /**
22031      * Clean up MS wordisms...
22032      */
22033     cleanWord : function(node)
22034     {
22035         if (!node) {
22036             this.cleanWord(this.doc.body);
22037             return;
22038         }
22039         
22040         if(
22041                 node.nodeName == 'SPAN' &&
22042                 !node.hasAttributes() &&
22043                 node.childNodes.length == 1 &&
22044                 node.firstChild.nodeName == "#text"  
22045         ) {
22046             var textNode = node.firstChild;
22047             node.removeChild(textNode);
22048             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22049                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
22050             }
22051             node.parentNode.insertBefore(textNode, node);
22052             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
22053                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
22054             }
22055             node.parentNode.removeChild(node);
22056         }
22057         
22058         if (node.nodeName == "#text") {
22059             // clean up silly Windows -- stuff?
22060             return; 
22061         }
22062         if (node.nodeName == "#comment") {
22063             node.parentNode.removeChild(node);
22064             // clean up silly Windows -- stuff?
22065             return; 
22066         }
22067         
22068         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22069             node.parentNode.removeChild(node);
22070             return;
22071         }
22072         //Roo.log(node.tagName);
22073         // remove - but keep children..
22074         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
22075             //Roo.log('-- removed');
22076             while (node.childNodes.length) {
22077                 var cn = node.childNodes[0];
22078                 node.removeChild(cn);
22079                 node.parentNode.insertBefore(cn, node);
22080                 // move node to parent - and clean it..
22081                 this.cleanWord(cn);
22082             }
22083             node.parentNode.removeChild(node);
22084             /// no need to iterate chidlren = it's got none..
22085             //this.iterateChildren(node, this.cleanWord);
22086             return;
22087         }
22088         // clean styles
22089         if (node.className.length) {
22090             
22091             var cn = node.className.split(/\W+/);
22092             var cna = [];
22093             Roo.each(cn, function(cls) {
22094                 if (cls.match(/Mso[a-zA-Z]+/)) {
22095                     return;
22096                 }
22097                 cna.push(cls);
22098             });
22099             node.className = cna.length ? cna.join(' ') : '';
22100             if (!cna.length) {
22101                 node.removeAttribute("class");
22102             }
22103         }
22104         
22105         if (node.hasAttribute("lang")) {
22106             node.removeAttribute("lang");
22107         }
22108         
22109         if (node.hasAttribute("style")) {
22110             
22111             var styles = node.getAttribute("style").split(";");
22112             var nstyle = [];
22113             Roo.each(styles, function(s) {
22114                 if (!s.match(/:/)) {
22115                     return;
22116                 }
22117                 var kv = s.split(":");
22118                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22119                     return;
22120                 }
22121                 // what ever is left... we allow.
22122                 nstyle.push(s);
22123             });
22124             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22125             if (!nstyle.length) {
22126                 node.removeAttribute('style');
22127             }
22128         }
22129         this.iterateChildren(node, this.cleanWord);
22130         
22131         
22132         
22133     },
22134     /**
22135      * iterateChildren of a Node, calling fn each time, using this as the scole..
22136      * @param {DomNode} node node to iterate children of.
22137      * @param {Function} fn method of this class to call on each item.
22138      */
22139     iterateChildren : function(node, fn)
22140     {
22141         if (!node.childNodes.length) {
22142                 return;
22143         }
22144         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22145            fn.call(this, node.childNodes[i])
22146         }
22147     },
22148     
22149     
22150     /**
22151      * cleanTableWidths.
22152      *
22153      * Quite often pasting from word etc.. results in tables with column and widths.
22154      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22155      *
22156      */
22157     cleanTableWidths : function(node)
22158     {
22159          
22160          
22161         if (!node) {
22162             this.cleanTableWidths(this.doc.body);
22163             return;
22164         }
22165         
22166         // ignore list...
22167         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22168             return; 
22169         }
22170         Roo.log(node.tagName);
22171         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22172             this.iterateChildren(node, this.cleanTableWidths);
22173             return;
22174         }
22175         if (node.hasAttribute('width')) {
22176             node.removeAttribute('width');
22177         }
22178         
22179          
22180         if (node.hasAttribute("style")) {
22181             // pretty basic...
22182             
22183             var styles = node.getAttribute("style").split(";");
22184             var nstyle = [];
22185             Roo.each(styles, function(s) {
22186                 if (!s.match(/:/)) {
22187                     return;
22188                 }
22189                 var kv = s.split(":");
22190                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22191                     return;
22192                 }
22193                 // what ever is left... we allow.
22194                 nstyle.push(s);
22195             });
22196             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22197             if (!nstyle.length) {
22198                 node.removeAttribute('style');
22199             }
22200         }
22201         
22202         this.iterateChildren(node, this.cleanTableWidths);
22203         
22204         
22205     },
22206     
22207     
22208     
22209     
22210     domToHTML : function(currentElement, depth, nopadtext) {
22211         
22212         depth = depth || 0;
22213         nopadtext = nopadtext || false;
22214     
22215         if (!currentElement) {
22216             return this.domToHTML(this.doc.body);
22217         }
22218         
22219         //Roo.log(currentElement);
22220         var j;
22221         var allText = false;
22222         var nodeName = currentElement.nodeName;
22223         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22224         
22225         if  (nodeName == '#text') {
22226             
22227             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22228         }
22229         
22230         
22231         var ret = '';
22232         if (nodeName != 'BODY') {
22233              
22234             var i = 0;
22235             // Prints the node tagName, such as <A>, <IMG>, etc
22236             if (tagName) {
22237                 var attr = [];
22238                 for(i = 0; i < currentElement.attributes.length;i++) {
22239                     // quoting?
22240                     var aname = currentElement.attributes.item(i).name;
22241                     if (!currentElement.attributes.item(i).value.length) {
22242                         continue;
22243                     }
22244                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22245                 }
22246                 
22247                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22248             } 
22249             else {
22250                 
22251                 // eack
22252             }
22253         } else {
22254             tagName = false;
22255         }
22256         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22257             return ret;
22258         }
22259         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22260             nopadtext = true;
22261         }
22262         
22263         
22264         // Traverse the tree
22265         i = 0;
22266         var currentElementChild = currentElement.childNodes.item(i);
22267         var allText = true;
22268         var innerHTML  = '';
22269         lastnode = '';
22270         while (currentElementChild) {
22271             // Formatting code (indent the tree so it looks nice on the screen)
22272             var nopad = nopadtext;
22273             if (lastnode == 'SPAN') {
22274                 nopad  = true;
22275             }
22276             // text
22277             if  (currentElementChild.nodeName == '#text') {
22278                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22279                 toadd = nopadtext ? toadd : toadd.trim();
22280                 if (!nopad && toadd.length > 80) {
22281                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22282                 }
22283                 innerHTML  += toadd;
22284                 
22285                 i++;
22286                 currentElementChild = currentElement.childNodes.item(i);
22287                 lastNode = '';
22288                 continue;
22289             }
22290             allText = false;
22291             
22292             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22293                 
22294             // Recursively traverse the tree structure of the child node
22295             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22296             lastnode = currentElementChild.nodeName;
22297             i++;
22298             currentElementChild=currentElement.childNodes.item(i);
22299         }
22300         
22301         ret += innerHTML;
22302         
22303         if (!allText) {
22304                 // The remaining code is mostly for formatting the tree
22305             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22306         }
22307         
22308         
22309         if (tagName) {
22310             ret+= "</"+tagName+">";
22311         }
22312         return ret;
22313         
22314     },
22315         
22316     applyBlacklists : function()
22317     {
22318         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22319         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22320         
22321         this.white = [];
22322         this.black = [];
22323         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22324             if (b.indexOf(tag) > -1) {
22325                 return;
22326             }
22327             this.white.push(tag);
22328             
22329         }, this);
22330         
22331         Roo.each(w, function(tag) {
22332             if (b.indexOf(tag) > -1) {
22333                 return;
22334             }
22335             if (this.white.indexOf(tag) > -1) {
22336                 return;
22337             }
22338             this.white.push(tag);
22339             
22340         }, this);
22341         
22342         
22343         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22344             if (w.indexOf(tag) > -1) {
22345                 return;
22346             }
22347             this.black.push(tag);
22348             
22349         }, this);
22350         
22351         Roo.each(b, function(tag) {
22352             if (w.indexOf(tag) > -1) {
22353                 return;
22354             }
22355             if (this.black.indexOf(tag) > -1) {
22356                 return;
22357             }
22358             this.black.push(tag);
22359             
22360         }, this);
22361         
22362         
22363         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22364         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22365         
22366         this.cwhite = [];
22367         this.cblack = [];
22368         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22369             if (b.indexOf(tag) > -1) {
22370                 return;
22371             }
22372             this.cwhite.push(tag);
22373             
22374         }, this);
22375         
22376         Roo.each(w, function(tag) {
22377             if (b.indexOf(tag) > -1) {
22378                 return;
22379             }
22380             if (this.cwhite.indexOf(tag) > -1) {
22381                 return;
22382             }
22383             this.cwhite.push(tag);
22384             
22385         }, this);
22386         
22387         
22388         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22389             if (w.indexOf(tag) > -1) {
22390                 return;
22391             }
22392             this.cblack.push(tag);
22393             
22394         }, this);
22395         
22396         Roo.each(b, function(tag) {
22397             if (w.indexOf(tag) > -1) {
22398                 return;
22399             }
22400             if (this.cblack.indexOf(tag) > -1) {
22401                 return;
22402             }
22403             this.cblack.push(tag);
22404             
22405         }, this);
22406     },
22407     
22408     setStylesheets : function(stylesheets)
22409     {
22410         if(typeof(stylesheets) == 'string'){
22411             Roo.get(this.iframe.contentDocument.head).createChild({
22412                 tag : 'link',
22413                 rel : 'stylesheet',
22414                 type : 'text/css',
22415                 href : stylesheets
22416             });
22417             
22418             return;
22419         }
22420         var _this = this;
22421      
22422         Roo.each(stylesheets, function(s) {
22423             if(!s.length){
22424                 return;
22425             }
22426             
22427             Roo.get(_this.iframe.contentDocument.head).createChild({
22428                 tag : 'link',
22429                 rel : 'stylesheet',
22430                 type : 'text/css',
22431                 href : s
22432             });
22433         });
22434
22435         
22436     },
22437     
22438     removeStylesheets : function()
22439     {
22440         var _this = this;
22441         
22442         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22443             s.remove();
22444         });
22445     },
22446     
22447     setStyle : function(style)
22448     {
22449         Roo.get(this.iframe.contentDocument.head).createChild({
22450             tag : 'style',
22451             type : 'text/css',
22452             html : style
22453         });
22454
22455         return;
22456     }
22457     
22458     // hide stuff that is not compatible
22459     /**
22460      * @event blur
22461      * @hide
22462      */
22463     /**
22464      * @event change
22465      * @hide
22466      */
22467     /**
22468      * @event focus
22469      * @hide
22470      */
22471     /**
22472      * @event specialkey
22473      * @hide
22474      */
22475     /**
22476      * @cfg {String} fieldClass @hide
22477      */
22478     /**
22479      * @cfg {String} focusClass @hide
22480      */
22481     /**
22482      * @cfg {String} autoCreate @hide
22483      */
22484     /**
22485      * @cfg {String} inputType @hide
22486      */
22487     /**
22488      * @cfg {String} invalidClass @hide
22489      */
22490     /**
22491      * @cfg {String} invalidText @hide
22492      */
22493     /**
22494      * @cfg {String} msgFx @hide
22495      */
22496     /**
22497      * @cfg {String} validateOnBlur @hide
22498      */
22499 });
22500
22501 Roo.HtmlEditorCore.white = [
22502         'area', 'br', 'img', 'input', 'hr', 'wbr',
22503         
22504        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22505        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22506        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22507        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22508        'table',   'ul',         'xmp', 
22509        
22510        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22511       'thead',   'tr', 
22512      
22513       'dir', 'menu', 'ol', 'ul', 'dl',
22514        
22515       'embed',  'object'
22516 ];
22517
22518
22519 Roo.HtmlEditorCore.black = [
22520     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22521         'applet', // 
22522         'base',   'basefont', 'bgsound', 'blink',  'body', 
22523         'frame',  'frameset', 'head',    'html',   'ilayer', 
22524         'iframe', 'layer',  'link',     'meta',    'object',   
22525         'script', 'style' ,'title',  'xml' // clean later..
22526 ];
22527 Roo.HtmlEditorCore.clean = [
22528     'script', 'style', 'title', 'xml'
22529 ];
22530 Roo.HtmlEditorCore.remove = [
22531     'font'
22532 ];
22533 // attributes..
22534
22535 Roo.HtmlEditorCore.ablack = [
22536     'on'
22537 ];
22538     
22539 Roo.HtmlEditorCore.aclean = [ 
22540     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22541 ];
22542
22543 // protocols..
22544 Roo.HtmlEditorCore.pwhite= [
22545         'http',  'https',  'mailto'
22546 ];
22547
22548 // white listed style attributes.
22549 Roo.HtmlEditorCore.cwhite= [
22550       //  'text-align', /// default is to allow most things..
22551       
22552          
22553 //        'font-size'//??
22554 ];
22555
22556 // black listed style attributes.
22557 Roo.HtmlEditorCore.cblack= [
22558       //  'font-size' -- this can be set by the project 
22559 ];
22560
22561
22562 Roo.HtmlEditorCore.swapCodes   =[ 
22563     [    8211, "--" ], 
22564     [    8212, "--" ], 
22565     [    8216,  "'" ],  
22566     [    8217, "'" ],  
22567     [    8220, '"' ],  
22568     [    8221, '"' ],  
22569     [    8226, "*" ],  
22570     [    8230, "..." ]
22571 ]; 
22572
22573     //<script type="text/javascript">
22574
22575 /*
22576  * Ext JS Library 1.1.1
22577  * Copyright(c) 2006-2007, Ext JS, LLC.
22578  * Licence LGPL
22579  * 
22580  */
22581  
22582  
22583 Roo.form.HtmlEditor = function(config){
22584     
22585     
22586     
22587     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22588     
22589     if (!this.toolbars) {
22590         this.toolbars = [];
22591     }
22592     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22593     
22594     
22595 };
22596
22597 /**
22598  * @class Roo.form.HtmlEditor
22599  * @extends Roo.form.Field
22600  * Provides a lightweight HTML Editor component.
22601  *
22602  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22603  * 
22604  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22605  * supported by this editor.</b><br/><br/>
22606  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22607  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22608  */
22609 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22610     /**
22611      * @cfg {Boolean} clearUp
22612      */
22613     clearUp : true,
22614       /**
22615      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22616      */
22617     toolbars : false,
22618    
22619      /**
22620      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22621      *                        Roo.resizable.
22622      */
22623     resizable : false,
22624      /**
22625      * @cfg {Number} height (in pixels)
22626      */   
22627     height: 300,
22628    /**
22629      * @cfg {Number} width (in pixels)
22630      */   
22631     width: 500,
22632     
22633     /**
22634      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22635      * 
22636      */
22637     stylesheets: false,
22638     
22639     
22640      /**
22641      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22642      * 
22643      */
22644     cblack: false,
22645     /**
22646      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22647      * 
22648      */
22649     cwhite: false,
22650     
22651      /**
22652      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22653      * 
22654      */
22655     black: false,
22656     /**
22657      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22658      * 
22659      */
22660     white: false,
22661     
22662     // id of frame..
22663     frameId: false,
22664     
22665     // private properties
22666     validationEvent : false,
22667     deferHeight: true,
22668     initialized : false,
22669     activated : false,
22670     
22671     onFocus : Roo.emptyFn,
22672     iframePad:3,
22673     hideMode:'offsets',
22674     
22675     actionMode : 'container', // defaults to hiding it...
22676     
22677     defaultAutoCreate : { // modified by initCompnoent..
22678         tag: "textarea",
22679         style:"width:500px;height:300px;",
22680         autocomplete: "new-password"
22681     },
22682
22683     // private
22684     initComponent : function(){
22685         this.addEvents({
22686             /**
22687              * @event initialize
22688              * Fires when the editor is fully initialized (including the iframe)
22689              * @param {HtmlEditor} this
22690              */
22691             initialize: true,
22692             /**
22693              * @event activate
22694              * Fires when the editor is first receives the focus. Any insertion must wait
22695              * until after this event.
22696              * @param {HtmlEditor} this
22697              */
22698             activate: true,
22699              /**
22700              * @event beforesync
22701              * Fires before the textarea is updated with content from the editor iframe. Return false
22702              * to cancel the sync.
22703              * @param {HtmlEditor} this
22704              * @param {String} html
22705              */
22706             beforesync: true,
22707              /**
22708              * @event beforepush
22709              * Fires before the iframe editor is updated with content from the textarea. Return false
22710              * to cancel the push.
22711              * @param {HtmlEditor} this
22712              * @param {String} html
22713              */
22714             beforepush: true,
22715              /**
22716              * @event sync
22717              * Fires when the textarea is updated with content from the editor iframe.
22718              * @param {HtmlEditor} this
22719              * @param {String} html
22720              */
22721             sync: true,
22722              /**
22723              * @event push
22724              * Fires when the iframe editor is updated with content from the textarea.
22725              * @param {HtmlEditor} this
22726              * @param {String} html
22727              */
22728             push: true,
22729              /**
22730              * @event editmodechange
22731              * Fires when the editor switches edit modes
22732              * @param {HtmlEditor} this
22733              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22734              */
22735             editmodechange: true,
22736             /**
22737              * @event editorevent
22738              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22739              * @param {HtmlEditor} this
22740              */
22741             editorevent: true,
22742             /**
22743              * @event firstfocus
22744              * Fires when on first focus - needed by toolbars..
22745              * @param {HtmlEditor} this
22746              */
22747             firstfocus: true,
22748             /**
22749              * @event autosave
22750              * Auto save the htmlEditor value as a file into Events
22751              * @param {HtmlEditor} this
22752              */
22753             autosave: true,
22754             /**
22755              * @event savedpreview
22756              * preview the saved version of htmlEditor
22757              * @param {HtmlEditor} this
22758              */
22759             savedpreview: true,
22760             
22761             /**
22762             * @event stylesheetsclick
22763             * Fires when press the Sytlesheets button
22764             * @param {Roo.HtmlEditorCore} this
22765             */
22766             stylesheetsclick: true
22767         });
22768         this.defaultAutoCreate =  {
22769             tag: "textarea",
22770             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22771             autocomplete: "new-password"
22772         };
22773     },
22774
22775     /**
22776      * Protected method that will not generally be called directly. It
22777      * is called when the editor creates its toolbar. Override this method if you need to
22778      * add custom toolbar buttons.
22779      * @param {HtmlEditor} editor
22780      */
22781     createToolbar : function(editor){
22782         Roo.log("create toolbars");
22783         if (!editor.toolbars || !editor.toolbars.length) {
22784             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22785         }
22786         
22787         for (var i =0 ; i < editor.toolbars.length;i++) {
22788             editor.toolbars[i] = Roo.factory(
22789                     typeof(editor.toolbars[i]) == 'string' ?
22790                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22791                 Roo.form.HtmlEditor);
22792             editor.toolbars[i].init(editor);
22793         }
22794          
22795         
22796     },
22797
22798      
22799     // private
22800     onRender : function(ct, position)
22801     {
22802         var _t = this;
22803         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22804         
22805         this.wrap = this.el.wrap({
22806             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22807         });
22808         
22809         this.editorcore.onRender(ct, position);
22810          
22811         if (this.resizable) {
22812             this.resizeEl = new Roo.Resizable(this.wrap, {
22813                 pinned : true,
22814                 wrap: true,
22815                 dynamic : true,
22816                 minHeight : this.height,
22817                 height: this.height,
22818                 handles : this.resizable,
22819                 width: this.width,
22820                 listeners : {
22821                     resize : function(r, w, h) {
22822                         _t.onResize(w,h); // -something
22823                     }
22824                 }
22825             });
22826             
22827         }
22828         this.createToolbar(this);
22829        
22830         
22831         if(!this.width){
22832             this.setSize(this.wrap.getSize());
22833         }
22834         if (this.resizeEl) {
22835             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22836             // should trigger onReize..
22837         }
22838         
22839         this.keyNav = new Roo.KeyNav(this.el, {
22840             
22841             "tab" : function(e){
22842                 e.preventDefault();
22843                 
22844                 var value = this.getValue();
22845                 
22846                 var start = this.el.dom.selectionStart;
22847                 var end = this.el.dom.selectionEnd;
22848                 
22849                 if(!e.shiftKey){
22850                     
22851                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22852                     this.el.dom.setSelectionRange(end + 1, end + 1);
22853                     return;
22854                 }
22855                 
22856                 var f = value.substring(0, start).split("\t");
22857                 
22858                 if(f.pop().length != 0){
22859                     return;
22860                 }
22861                 
22862                 this.setValue(f.join("\t") + value.substring(end));
22863                 this.el.dom.setSelectionRange(start - 1, start - 1);
22864                 
22865             },
22866             
22867             "home" : function(e){
22868                 e.preventDefault();
22869                 
22870                 var curr = this.el.dom.selectionStart;
22871                 var lines = this.getValue().split("\n");
22872                 
22873                 if(!lines.length){
22874                     return;
22875                 }
22876                 
22877                 if(e.ctrlKey){
22878                     this.el.dom.setSelectionRange(0, 0);
22879                     return;
22880                 }
22881                 
22882                 var pos = 0;
22883                 
22884                 for (var i = 0; i < lines.length;i++) {
22885                     pos += lines[i].length;
22886                     
22887                     if(i != 0){
22888                         pos += 1;
22889                     }
22890                     
22891                     if(pos < curr){
22892                         continue;
22893                     }
22894                     
22895                     pos -= lines[i].length;
22896                     
22897                     break;
22898                 }
22899                 
22900                 if(!e.shiftKey){
22901                     this.el.dom.setSelectionRange(pos, pos);
22902                     return;
22903                 }
22904                 
22905                 this.el.dom.selectionStart = pos;
22906                 this.el.dom.selectionEnd = curr;
22907             },
22908             
22909             "end" : function(e){
22910                 e.preventDefault();
22911                 
22912                 var curr = this.el.dom.selectionStart;
22913                 var lines = this.getValue().split("\n");
22914                 
22915                 if(!lines.length){
22916                     return;
22917                 }
22918                 
22919                 if(e.ctrlKey){
22920                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22921                     return;
22922                 }
22923                 
22924                 var pos = 0;
22925                 
22926                 for (var i = 0; i < lines.length;i++) {
22927                     
22928                     pos += lines[i].length;
22929                     
22930                     if(i != 0){
22931                         pos += 1;
22932                     }
22933                     
22934                     if(pos < curr){
22935                         continue;
22936                     }
22937                     
22938                     break;
22939                 }
22940                 
22941                 if(!e.shiftKey){
22942                     this.el.dom.setSelectionRange(pos, pos);
22943                     return;
22944                 }
22945                 
22946                 this.el.dom.selectionStart = curr;
22947                 this.el.dom.selectionEnd = pos;
22948             },
22949
22950             scope : this,
22951
22952             doRelay : function(foo, bar, hname){
22953                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22954             },
22955
22956             forceKeyDown: true
22957         });
22958         
22959 //        if(this.autosave && this.w){
22960 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22961 //        }
22962     },
22963
22964     // private
22965     onResize : function(w, h)
22966     {
22967         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22968         var ew = false;
22969         var eh = false;
22970         
22971         if(this.el ){
22972             if(typeof w == 'number'){
22973                 var aw = w - this.wrap.getFrameWidth('lr');
22974                 this.el.setWidth(this.adjustWidth('textarea', aw));
22975                 ew = aw;
22976             }
22977             if(typeof h == 'number'){
22978                 var tbh = 0;
22979                 for (var i =0; i < this.toolbars.length;i++) {
22980                     // fixme - ask toolbars for heights?
22981                     tbh += this.toolbars[i].tb.el.getHeight();
22982                     if (this.toolbars[i].footer) {
22983                         tbh += this.toolbars[i].footer.el.getHeight();
22984                     }
22985                 }
22986                 
22987                 
22988                 
22989                 
22990                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22991                 ah -= 5; // knock a few pixes off for look..
22992 //                Roo.log(ah);
22993                 this.el.setHeight(this.adjustWidth('textarea', ah));
22994                 var eh = ah;
22995             }
22996         }
22997         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22998         this.editorcore.onResize(ew,eh);
22999         
23000     },
23001
23002     /**
23003      * Toggles the editor between standard and source edit mode.
23004      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23005      */
23006     toggleSourceEdit : function(sourceEditMode)
23007     {
23008         this.editorcore.toggleSourceEdit(sourceEditMode);
23009         
23010         if(this.editorcore.sourceEditMode){
23011             Roo.log('editor - showing textarea');
23012             
23013 //            Roo.log('in');
23014 //            Roo.log(this.syncValue());
23015             this.editorcore.syncValue();
23016             this.el.removeClass('x-hidden');
23017             this.el.dom.removeAttribute('tabIndex');
23018             this.el.focus();
23019             
23020             for (var i = 0; i < this.toolbars.length; i++) {
23021                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23022                     this.toolbars[i].tb.hide();
23023                     this.toolbars[i].footer.hide();
23024                 }
23025             }
23026             
23027         }else{
23028             Roo.log('editor - hiding textarea');
23029 //            Roo.log('out')
23030 //            Roo.log(this.pushValue()); 
23031             this.editorcore.pushValue();
23032             
23033             this.el.addClass('x-hidden');
23034             this.el.dom.setAttribute('tabIndex', -1);
23035             
23036             for (var i = 0; i < this.toolbars.length; i++) {
23037                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
23038                     this.toolbars[i].tb.show();
23039                     this.toolbars[i].footer.show();
23040                 }
23041             }
23042             
23043             //this.deferFocus();
23044         }
23045         
23046         this.setSize(this.wrap.getSize());
23047         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
23048         
23049         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23050     },
23051  
23052     // private (for BoxComponent)
23053     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23054
23055     // private (for BoxComponent)
23056     getResizeEl : function(){
23057         return this.wrap;
23058     },
23059
23060     // private (for BoxComponent)
23061     getPositionEl : function(){
23062         return this.wrap;
23063     },
23064
23065     // private
23066     initEvents : function(){
23067         this.originalValue = this.getValue();
23068     },
23069
23070     /**
23071      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23072      * @method
23073      */
23074     markInvalid : Roo.emptyFn,
23075     /**
23076      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23077      * @method
23078      */
23079     clearInvalid : Roo.emptyFn,
23080
23081     setValue : function(v){
23082         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
23083         this.editorcore.pushValue();
23084     },
23085
23086      
23087     // private
23088     deferFocus : function(){
23089         this.focus.defer(10, this);
23090     },
23091
23092     // doc'ed in Field
23093     focus : function(){
23094         this.editorcore.focus();
23095         
23096     },
23097       
23098
23099     // private
23100     onDestroy : function(){
23101         
23102         
23103         
23104         if(this.rendered){
23105             
23106             for (var i =0; i < this.toolbars.length;i++) {
23107                 // fixme - ask toolbars for heights?
23108                 this.toolbars[i].onDestroy();
23109             }
23110             
23111             this.wrap.dom.innerHTML = '';
23112             this.wrap.remove();
23113         }
23114     },
23115
23116     // private
23117     onFirstFocus : function(){
23118         //Roo.log("onFirstFocus");
23119         this.editorcore.onFirstFocus();
23120          for (var i =0; i < this.toolbars.length;i++) {
23121             this.toolbars[i].onFirstFocus();
23122         }
23123         
23124     },
23125     
23126     // private
23127     syncValue : function()
23128     {
23129         this.editorcore.syncValue();
23130     },
23131     
23132     pushValue : function()
23133     {
23134         this.editorcore.pushValue();
23135     },
23136     
23137     setStylesheets : function(stylesheets)
23138     {
23139         this.editorcore.setStylesheets(stylesheets);
23140     },
23141     
23142     removeStylesheets : function()
23143     {
23144         this.editorcore.removeStylesheets();
23145     }
23146      
23147     
23148     // hide stuff that is not compatible
23149     /**
23150      * @event blur
23151      * @hide
23152      */
23153     /**
23154      * @event change
23155      * @hide
23156      */
23157     /**
23158      * @event focus
23159      * @hide
23160      */
23161     /**
23162      * @event specialkey
23163      * @hide
23164      */
23165     /**
23166      * @cfg {String} fieldClass @hide
23167      */
23168     /**
23169      * @cfg {String} focusClass @hide
23170      */
23171     /**
23172      * @cfg {String} autoCreate @hide
23173      */
23174     /**
23175      * @cfg {String} inputType @hide
23176      */
23177     /**
23178      * @cfg {String} invalidClass @hide
23179      */
23180     /**
23181      * @cfg {String} invalidText @hide
23182      */
23183     /**
23184      * @cfg {String} msgFx @hide
23185      */
23186     /**
23187      * @cfg {String} validateOnBlur @hide
23188      */
23189 });
23190  
23191     // <script type="text/javascript">
23192 /*
23193  * Based on
23194  * Ext JS Library 1.1.1
23195  * Copyright(c) 2006-2007, Ext JS, LLC.
23196  *  
23197  
23198  */
23199
23200 /**
23201  * @class Roo.form.HtmlEditorToolbar1
23202  * Basic Toolbar
23203  * 
23204  * Usage:
23205  *
23206  new Roo.form.HtmlEditor({
23207     ....
23208     toolbars : [
23209         new Roo.form.HtmlEditorToolbar1({
23210             disable : { fonts: 1 , format: 1, ..., ... , ...],
23211             btns : [ .... ]
23212         })
23213     }
23214      
23215  * 
23216  * @cfg {Object} disable List of elements to disable..
23217  * @cfg {Array} btns List of additional buttons.
23218  * 
23219  * 
23220  * NEEDS Extra CSS? 
23221  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23222  */
23223  
23224 Roo.form.HtmlEditor.ToolbarStandard = function(config)
23225 {
23226     
23227     Roo.apply(this, config);
23228     
23229     // default disabled, based on 'good practice'..
23230     this.disable = this.disable || {};
23231     Roo.applyIf(this.disable, {
23232         fontSize : true,
23233         colors : true,
23234         specialElements : true
23235     });
23236     
23237     
23238     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23239     // dont call parent... till later.
23240 }
23241
23242 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
23243     
23244     tb: false,
23245     
23246     rendered: false,
23247     
23248     editor : false,
23249     editorcore : false,
23250     /**
23251      * @cfg {Object} disable  List of toolbar elements to disable
23252          
23253      */
23254     disable : false,
23255     
23256     
23257      /**
23258      * @cfg {String} createLinkText The default text for the create link prompt
23259      */
23260     createLinkText : 'Please enter the URL for the link:',
23261     /**
23262      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
23263      */
23264     defaultLinkValue : 'http:/'+'/',
23265    
23266     
23267       /**
23268      * @cfg {Array} fontFamilies An array of available font families
23269      */
23270     fontFamilies : [
23271         'Arial',
23272         'Courier New',
23273         'Tahoma',
23274         'Times New Roman',
23275         'Verdana'
23276     ],
23277     
23278     specialChars : [
23279            "&#169;",
23280           "&#174;",     
23281           "&#8482;",    
23282           "&#163;" ,    
23283          // "&#8212;",    
23284           "&#8230;",    
23285           "&#247;" ,    
23286         //  "&#225;" ,     ?? a acute?
23287            "&#8364;"    , //Euro
23288        //   "&#8220;"    ,
23289         //  "&#8221;"    ,
23290         //  "&#8226;"    ,
23291           "&#176;"  //   , // degrees
23292
23293          // "&#233;"     , // e ecute
23294          // "&#250;"     , // u ecute?
23295     ],
23296     
23297     specialElements : [
23298         {
23299             text: "Insert Table",
23300             xtype: 'MenuItem',
23301             xns : Roo.Menu,
23302             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
23303                 
23304         },
23305         {    
23306             text: "Insert Image",
23307             xtype: 'MenuItem',
23308             xns : Roo.Menu,
23309             ihtml : '<img src="about:blank"/>'
23310             
23311         }
23312         
23313          
23314     ],
23315     
23316     
23317     inputElements : [ 
23318             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
23319             "input:submit", "input:button", "select", "textarea", "label" ],
23320     formats : [
23321         ["p"] ,  
23322         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
23323         ["pre"],[ "code"], 
23324         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
23325         ['div'],['span'],
23326         ['sup'],['sub']
23327     ],
23328     
23329     cleanStyles : [
23330         "font-size"
23331     ],
23332      /**
23333      * @cfg {String} defaultFont default font to use.
23334      */
23335     defaultFont: 'tahoma',
23336    
23337     fontSelect : false,
23338     
23339     
23340     formatCombo : false,
23341     
23342     init : function(editor)
23343     {
23344         this.editor = editor;
23345         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23346         var editorcore = this.editorcore;
23347         
23348         var _t = this;
23349         
23350         var fid = editorcore.frameId;
23351         var etb = this;
23352         function btn(id, toggle, handler){
23353             var xid = fid + '-'+ id ;
23354             return {
23355                 id : xid,
23356                 cmd : id,
23357                 cls : 'x-btn-icon x-edit-'+id,
23358                 enableToggle:toggle !== false,
23359                 scope: _t, // was editor...
23360                 handler:handler||_t.relayBtnCmd,
23361                 clickEvent:'mousedown',
23362                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23363                 tabIndex:-1
23364             };
23365         }
23366         
23367         
23368         
23369         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23370         this.tb = tb;
23371          // stop form submits
23372         tb.el.on('click', function(e){
23373             e.preventDefault(); // what does this do?
23374         });
23375
23376         if(!this.disable.font) { // && !Roo.isSafari){
23377             /* why no safari for fonts 
23378             editor.fontSelect = tb.el.createChild({
23379                 tag:'select',
23380                 tabIndex: -1,
23381                 cls:'x-font-select',
23382                 html: this.createFontOptions()
23383             });
23384             
23385             editor.fontSelect.on('change', function(){
23386                 var font = editor.fontSelect.dom.value;
23387                 editor.relayCmd('fontname', font);
23388                 editor.deferFocus();
23389             }, editor);
23390             
23391             tb.add(
23392                 editor.fontSelect.dom,
23393                 '-'
23394             );
23395             */
23396             
23397         };
23398         if(!this.disable.formats){
23399             this.formatCombo = new Roo.form.ComboBox({
23400                 store: new Roo.data.SimpleStore({
23401                     id : 'tag',
23402                     fields: ['tag'],
23403                     data : this.formats // from states.js
23404                 }),
23405                 blockFocus : true,
23406                 name : '',
23407                 //autoCreate : {tag: "div",  size: "20"},
23408                 displayField:'tag',
23409                 typeAhead: false,
23410                 mode: 'local',
23411                 editable : false,
23412                 triggerAction: 'all',
23413                 emptyText:'Add tag',
23414                 selectOnFocus:true,
23415                 width:135,
23416                 listeners : {
23417                     'select': function(c, r, i) {
23418                         editorcore.insertTag(r.get('tag'));
23419                         editor.focus();
23420                     }
23421                 }
23422
23423             });
23424             tb.addField(this.formatCombo);
23425             
23426         }
23427         
23428         if(!this.disable.format){
23429             tb.add(
23430                 btn('bold'),
23431                 btn('italic'),
23432                 btn('underline'),
23433                 btn('strikethrough')
23434             );
23435         };
23436         if(!this.disable.fontSize){
23437             tb.add(
23438                 '-',
23439                 
23440                 
23441                 btn('increasefontsize', false, editorcore.adjustFont),
23442                 btn('decreasefontsize', false, editorcore.adjustFont)
23443             );
23444         };
23445         
23446         
23447         if(!this.disable.colors){
23448             tb.add(
23449                 '-', {
23450                     id:editorcore.frameId +'-forecolor',
23451                     cls:'x-btn-icon x-edit-forecolor',
23452                     clickEvent:'mousedown',
23453                     tooltip: this.buttonTips['forecolor'] || undefined,
23454                     tabIndex:-1,
23455                     menu : new Roo.menu.ColorMenu({
23456                         allowReselect: true,
23457                         focus: Roo.emptyFn,
23458                         value:'000000',
23459                         plain:true,
23460                         selectHandler: function(cp, color){
23461                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23462                             editor.deferFocus();
23463                         },
23464                         scope: editorcore,
23465                         clickEvent:'mousedown'
23466                     })
23467                 }, {
23468                     id:editorcore.frameId +'backcolor',
23469                     cls:'x-btn-icon x-edit-backcolor',
23470                     clickEvent:'mousedown',
23471                     tooltip: this.buttonTips['backcolor'] || undefined,
23472                     tabIndex:-1,
23473                     menu : new Roo.menu.ColorMenu({
23474                         focus: Roo.emptyFn,
23475                         value:'FFFFFF',
23476                         plain:true,
23477                         allowReselect: true,
23478                         selectHandler: function(cp, color){
23479                             if(Roo.isGecko){
23480                                 editorcore.execCmd('useCSS', false);
23481                                 editorcore.execCmd('hilitecolor', color);
23482                                 editorcore.execCmd('useCSS', true);
23483                                 editor.deferFocus();
23484                             }else{
23485                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23486                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23487                                 editor.deferFocus();
23488                             }
23489                         },
23490                         scope:editorcore,
23491                         clickEvent:'mousedown'
23492                     })
23493                 }
23494             );
23495         };
23496         // now add all the items...
23497         
23498
23499         if(!this.disable.alignments){
23500             tb.add(
23501                 '-',
23502                 btn('justifyleft'),
23503                 btn('justifycenter'),
23504                 btn('justifyright')
23505             );
23506         };
23507
23508         //if(!Roo.isSafari){
23509             if(!this.disable.links){
23510                 tb.add(
23511                     '-',
23512                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23513                 );
23514             };
23515
23516             if(!this.disable.lists){
23517                 tb.add(
23518                     '-',
23519                     btn('insertorderedlist'),
23520                     btn('insertunorderedlist')
23521                 );
23522             }
23523             if(!this.disable.sourceEdit){
23524                 tb.add(
23525                     '-',
23526                     btn('sourceedit', true, function(btn){
23527                         this.toggleSourceEdit(btn.pressed);
23528                     })
23529                 );
23530             }
23531         //}
23532         
23533         var smenu = { };
23534         // special menu.. - needs to be tidied up..
23535         if (!this.disable.special) {
23536             smenu = {
23537                 text: "&#169;",
23538                 cls: 'x-edit-none',
23539                 
23540                 menu : {
23541                     items : []
23542                 }
23543             };
23544             for (var i =0; i < this.specialChars.length; i++) {
23545                 smenu.menu.items.push({
23546                     
23547                     html: this.specialChars[i],
23548                     handler: function(a,b) {
23549                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23550                         //editor.insertAtCursor(a.html);
23551                         
23552                     },
23553                     tabIndex:-1
23554                 });
23555             }
23556             
23557             
23558             tb.add(smenu);
23559             
23560             
23561         }
23562         
23563         var cmenu = { };
23564         if (!this.disable.cleanStyles) {
23565             cmenu = {
23566                 cls: 'x-btn-icon x-btn-clear',
23567                 
23568                 menu : {
23569                     items : []
23570                 }
23571             };
23572             for (var i =0; i < this.cleanStyles.length; i++) {
23573                 cmenu.menu.items.push({
23574                     actiontype : this.cleanStyles[i],
23575                     html: 'Remove ' + this.cleanStyles[i],
23576                     handler: function(a,b) {
23577 //                        Roo.log(a);
23578 //                        Roo.log(b);
23579                         var c = Roo.get(editorcore.doc.body);
23580                         c.select('[style]').each(function(s) {
23581                             s.dom.style.removeProperty(a.actiontype);
23582                         });
23583                         editorcore.syncValue();
23584                     },
23585                     tabIndex:-1
23586                 });
23587             }
23588              cmenu.menu.items.push({
23589                 actiontype : 'tablewidths',
23590                 html: 'Remove Table Widths',
23591                 handler: function(a,b) {
23592                     editorcore.cleanTableWidths();
23593                     editorcore.syncValue();
23594                 },
23595                 tabIndex:-1
23596             });
23597             cmenu.menu.items.push({
23598                 actiontype : 'word',
23599                 html: 'Remove MS Word Formating',
23600                 handler: function(a,b) {
23601                     editorcore.cleanWord();
23602                     editorcore.syncValue();
23603                 },
23604                 tabIndex:-1
23605             });
23606             
23607             cmenu.menu.items.push({
23608                 actiontype : 'all',
23609                 html: 'Remove All Styles',
23610                 handler: function(a,b) {
23611                     
23612                     var c = Roo.get(editorcore.doc.body);
23613                     c.select('[style]').each(function(s) {
23614                         s.dom.removeAttribute('style');
23615                     });
23616                     editorcore.syncValue();
23617                 },
23618                 tabIndex:-1
23619             });
23620             
23621             cmenu.menu.items.push({
23622                 actiontype : 'all',
23623                 html: 'Remove All CSS Classes',
23624                 handler: function(a,b) {
23625                     
23626                     var c = Roo.get(editorcore.doc.body);
23627                     c.select('[class]').each(function(s) {
23628                         s.dom.removeAttribute('class');
23629                     });
23630                     editorcore.cleanWord();
23631                     editorcore.syncValue();
23632                 },
23633                 tabIndex:-1
23634             });
23635             
23636              cmenu.menu.items.push({
23637                 actiontype : 'tidy',
23638                 html: 'Tidy HTML Source',
23639                 handler: function(a,b) {
23640                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23641                     editorcore.syncValue();
23642                 },
23643                 tabIndex:-1
23644             });
23645             
23646             
23647             tb.add(cmenu);
23648         }
23649          
23650         if (!this.disable.specialElements) {
23651             var semenu = {
23652                 text: "Other;",
23653                 cls: 'x-edit-none',
23654                 menu : {
23655                     items : []
23656                 }
23657             };
23658             for (var i =0; i < this.specialElements.length; i++) {
23659                 semenu.menu.items.push(
23660                     Roo.apply({ 
23661                         handler: function(a,b) {
23662                             editor.insertAtCursor(this.ihtml);
23663                         }
23664                     }, this.specialElements[i])
23665                 );
23666                     
23667             }
23668             
23669             tb.add(semenu);
23670             
23671             
23672         }
23673          
23674         
23675         if (this.btns) {
23676             for(var i =0; i< this.btns.length;i++) {
23677                 var b = Roo.factory(this.btns[i],Roo.form);
23678                 b.cls =  'x-edit-none';
23679                 
23680                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23681                     b.cls += ' x-init-enable';
23682                 }
23683                 
23684                 b.scope = editorcore;
23685                 tb.add(b);
23686             }
23687         
23688         }
23689         
23690         
23691         
23692         // disable everything...
23693         
23694         this.tb.items.each(function(item){
23695             
23696            if(
23697                 item.id != editorcore.frameId+ '-sourceedit' && 
23698                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23699             ){
23700                 
23701                 item.disable();
23702             }
23703         });
23704         this.rendered = true;
23705         
23706         // the all the btns;
23707         editor.on('editorevent', this.updateToolbar, this);
23708         // other toolbars need to implement this..
23709         //editor.on('editmodechange', this.updateToolbar, this);
23710     },
23711     
23712     
23713     relayBtnCmd : function(btn) {
23714         this.editorcore.relayCmd(btn.cmd);
23715     },
23716     // private used internally
23717     createLink : function(){
23718         Roo.log("create link?");
23719         var url = prompt(this.createLinkText, this.defaultLinkValue);
23720         if(url && url != 'http:/'+'/'){
23721             this.editorcore.relayCmd('createlink', url);
23722         }
23723     },
23724
23725     
23726     /**
23727      * Protected method that will not generally be called directly. It triggers
23728      * a toolbar update by reading the markup state of the current selection in the editor.
23729      */
23730     updateToolbar: function(){
23731
23732         if(!this.editorcore.activated){
23733             this.editor.onFirstFocus();
23734             return;
23735         }
23736
23737         var btns = this.tb.items.map, 
23738             doc = this.editorcore.doc,
23739             frameId = this.editorcore.frameId;
23740
23741         if(!this.disable.font && !Roo.isSafari){
23742             /*
23743             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23744             if(name != this.fontSelect.dom.value){
23745                 this.fontSelect.dom.value = name;
23746             }
23747             */
23748         }
23749         if(!this.disable.format){
23750             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23751             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23752             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23753             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23754         }
23755         if(!this.disable.alignments){
23756             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23757             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23758             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23759         }
23760         if(!Roo.isSafari && !this.disable.lists){
23761             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23762             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23763         }
23764         
23765         var ans = this.editorcore.getAllAncestors();
23766         if (this.formatCombo) {
23767             
23768             
23769             var store = this.formatCombo.store;
23770             this.formatCombo.setValue("");
23771             for (var i =0; i < ans.length;i++) {
23772                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23773                     // select it..
23774                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23775                     break;
23776                 }
23777             }
23778         }
23779         
23780         
23781         
23782         // hides menus... - so this cant be on a menu...
23783         Roo.menu.MenuMgr.hideAll();
23784
23785         //this.editorsyncValue();
23786     },
23787    
23788     
23789     createFontOptions : function(){
23790         var buf = [], fs = this.fontFamilies, ff, lc;
23791         
23792         
23793         
23794         for(var i = 0, len = fs.length; i< len; i++){
23795             ff = fs[i];
23796             lc = ff.toLowerCase();
23797             buf.push(
23798                 '<option value="',lc,'" style="font-family:',ff,';"',
23799                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23800                     ff,
23801                 '</option>'
23802             );
23803         }
23804         return buf.join('');
23805     },
23806     
23807     toggleSourceEdit : function(sourceEditMode){
23808         
23809         Roo.log("toolbar toogle");
23810         if(sourceEditMode === undefined){
23811             sourceEditMode = !this.sourceEditMode;
23812         }
23813         this.sourceEditMode = sourceEditMode === true;
23814         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23815         // just toggle the button?
23816         if(btn.pressed !== this.sourceEditMode){
23817             btn.toggle(this.sourceEditMode);
23818             return;
23819         }
23820         
23821         if(sourceEditMode){
23822             Roo.log("disabling buttons");
23823             this.tb.items.each(function(item){
23824                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23825                     item.disable();
23826                 }
23827             });
23828           
23829         }else{
23830             Roo.log("enabling buttons");
23831             if(this.editorcore.initialized){
23832                 this.tb.items.each(function(item){
23833                     item.enable();
23834                 });
23835             }
23836             
23837         }
23838         Roo.log("calling toggole on editor");
23839         // tell the editor that it's been pressed..
23840         this.editor.toggleSourceEdit(sourceEditMode);
23841        
23842     },
23843      /**
23844      * Object collection of toolbar tooltips for the buttons in the editor. The key
23845      * is the command id associated with that button and the value is a valid QuickTips object.
23846      * For example:
23847 <pre><code>
23848 {
23849     bold : {
23850         title: 'Bold (Ctrl+B)',
23851         text: 'Make the selected text bold.',
23852         cls: 'x-html-editor-tip'
23853     },
23854     italic : {
23855         title: 'Italic (Ctrl+I)',
23856         text: 'Make the selected text italic.',
23857         cls: 'x-html-editor-tip'
23858     },
23859     ...
23860 </code></pre>
23861     * @type Object
23862      */
23863     buttonTips : {
23864         bold : {
23865             title: 'Bold (Ctrl+B)',
23866             text: 'Make the selected text bold.',
23867             cls: 'x-html-editor-tip'
23868         },
23869         italic : {
23870             title: 'Italic (Ctrl+I)',
23871             text: 'Make the selected text italic.',
23872             cls: 'x-html-editor-tip'
23873         },
23874         underline : {
23875             title: 'Underline (Ctrl+U)',
23876             text: 'Underline the selected text.',
23877             cls: 'x-html-editor-tip'
23878         },
23879         strikethrough : {
23880             title: 'Strikethrough',
23881             text: 'Strikethrough the selected text.',
23882             cls: 'x-html-editor-tip'
23883         },
23884         increasefontsize : {
23885             title: 'Grow Text',
23886             text: 'Increase the font size.',
23887             cls: 'x-html-editor-tip'
23888         },
23889         decreasefontsize : {
23890             title: 'Shrink Text',
23891             text: 'Decrease the font size.',
23892             cls: 'x-html-editor-tip'
23893         },
23894         backcolor : {
23895             title: 'Text Highlight Color',
23896             text: 'Change the background color of the selected text.',
23897             cls: 'x-html-editor-tip'
23898         },
23899         forecolor : {
23900             title: 'Font Color',
23901             text: 'Change the color of the selected text.',
23902             cls: 'x-html-editor-tip'
23903         },
23904         justifyleft : {
23905             title: 'Align Text Left',
23906             text: 'Align text to the left.',
23907             cls: 'x-html-editor-tip'
23908         },
23909         justifycenter : {
23910             title: 'Center Text',
23911             text: 'Center text in the editor.',
23912             cls: 'x-html-editor-tip'
23913         },
23914         justifyright : {
23915             title: 'Align Text Right',
23916             text: 'Align text to the right.',
23917             cls: 'x-html-editor-tip'
23918         },
23919         insertunorderedlist : {
23920             title: 'Bullet List',
23921             text: 'Start a bulleted list.',
23922             cls: 'x-html-editor-tip'
23923         },
23924         insertorderedlist : {
23925             title: 'Numbered List',
23926             text: 'Start a numbered list.',
23927             cls: 'x-html-editor-tip'
23928         },
23929         createlink : {
23930             title: 'Hyperlink',
23931             text: 'Make the selected text a hyperlink.',
23932             cls: 'x-html-editor-tip'
23933         },
23934         sourceedit : {
23935             title: 'Source Edit',
23936             text: 'Switch to source editing mode.',
23937             cls: 'x-html-editor-tip'
23938         }
23939     },
23940     // private
23941     onDestroy : function(){
23942         if(this.rendered){
23943             
23944             this.tb.items.each(function(item){
23945                 if(item.menu){
23946                     item.menu.removeAll();
23947                     if(item.menu.el){
23948                         item.menu.el.destroy();
23949                     }
23950                 }
23951                 item.destroy();
23952             });
23953              
23954         }
23955     },
23956     onFirstFocus: function() {
23957         this.tb.items.each(function(item){
23958            item.enable();
23959         });
23960     }
23961 });
23962
23963
23964
23965
23966 // <script type="text/javascript">
23967 /*
23968  * Based on
23969  * Ext JS Library 1.1.1
23970  * Copyright(c) 2006-2007, Ext JS, LLC.
23971  *  
23972  
23973  */
23974
23975  
23976 /**
23977  * @class Roo.form.HtmlEditor.ToolbarContext
23978  * Context Toolbar
23979  * 
23980  * Usage:
23981  *
23982  new Roo.form.HtmlEditor({
23983     ....
23984     toolbars : [
23985         { xtype: 'ToolbarStandard', styles : {} }
23986         { xtype: 'ToolbarContext', disable : {} }
23987     ]
23988 })
23989
23990      
23991  * 
23992  * @config : {Object} disable List of elements to disable.. (not done yet.)
23993  * @config : {Object} styles  Map of styles available.
23994  * 
23995  */
23996
23997 Roo.form.HtmlEditor.ToolbarContext = function(config)
23998 {
23999     
24000     Roo.apply(this, config);
24001     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
24002     // dont call parent... till later.
24003     this.styles = this.styles || {};
24004 }
24005
24006  
24007
24008 Roo.form.HtmlEditor.ToolbarContext.types = {
24009     'IMG' : {
24010         width : {
24011             title: "Width",
24012             width: 40
24013         },
24014         height:  {
24015             title: "Height",
24016             width: 40
24017         },
24018         align: {
24019             title: "Align",
24020             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
24021             width : 80
24022             
24023         },
24024         border: {
24025             title: "Border",
24026             width: 40
24027         },
24028         alt: {
24029             title: "Alt",
24030             width: 120
24031         },
24032         src : {
24033             title: "Src",
24034             width: 220
24035         }
24036         
24037     },
24038     'A' : {
24039         name : {
24040             title: "Name",
24041             width: 50
24042         },
24043         target:  {
24044             title: "Target",
24045             width: 120
24046         },
24047         href:  {
24048             title: "Href",
24049             width: 220
24050         } // border?
24051         
24052     },
24053     'TABLE' : {
24054         rows : {
24055             title: "Rows",
24056             width: 20
24057         },
24058         cols : {
24059             title: "Cols",
24060             width: 20
24061         },
24062         width : {
24063             title: "Width",
24064             width: 40
24065         },
24066         height : {
24067             title: "Height",
24068             width: 40
24069         },
24070         border : {
24071             title: "Border",
24072             width: 20
24073         }
24074     },
24075     'TD' : {
24076         width : {
24077             title: "Width",
24078             width: 40
24079         },
24080         height : {
24081             title: "Height",
24082             width: 40
24083         },   
24084         align: {
24085             title: "Align",
24086             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
24087             width: 80
24088         },
24089         valign: {
24090             title: "Valign",
24091             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
24092             width: 80
24093         },
24094         colspan: {
24095             title: "Colspan",
24096             width: 20
24097             
24098         },
24099          'font-family'  : {
24100             title : "Font",
24101             style : 'fontFamily',
24102             displayField: 'display',
24103             optname : 'font-family',
24104             width: 140
24105         }
24106     },
24107     'INPUT' : {
24108         name : {
24109             title: "name",
24110             width: 120
24111         },
24112         value : {
24113             title: "Value",
24114             width: 120
24115         },
24116         width : {
24117             title: "Width",
24118             width: 40
24119         }
24120     },
24121     'LABEL' : {
24122         'for' : {
24123             title: "For",
24124             width: 120
24125         }
24126     },
24127     'TEXTAREA' : {
24128           name : {
24129             title: "name",
24130             width: 120
24131         },
24132         rows : {
24133             title: "Rows",
24134             width: 20
24135         },
24136         cols : {
24137             title: "Cols",
24138             width: 20
24139         }
24140     },
24141     'SELECT' : {
24142         name : {
24143             title: "name",
24144             width: 120
24145         },
24146         selectoptions : {
24147             title: "Options",
24148             width: 200
24149         }
24150     },
24151     
24152     // should we really allow this??
24153     // should this just be 
24154     'BODY' : {
24155         title : {
24156             title: "Title",
24157             width: 200,
24158             disabled : true
24159         }
24160     },
24161     'SPAN' : {
24162         'font-family'  : {
24163             title : "Font",
24164             style : 'fontFamily',
24165             displayField: 'display',
24166             optname : 'font-family',
24167             width: 140
24168         }
24169     },
24170     'DIV' : {
24171         'font-family'  : {
24172             title : "Font",
24173             style : 'fontFamily',
24174             displayField: 'display',
24175             optname : 'font-family',
24176             width: 140
24177         }
24178     },
24179      'P' : {
24180         'font-family'  : {
24181             title : "Font",
24182             style : 'fontFamily',
24183             displayField: 'display',
24184             optname : 'font-family',
24185             width: 140
24186         }
24187     },
24188     
24189     '*' : {
24190         // empty..
24191     }
24192
24193 };
24194
24195 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
24196 Roo.form.HtmlEditor.ToolbarContext.stores = false;
24197
24198 Roo.form.HtmlEditor.ToolbarContext.options = {
24199         'font-family'  : [ 
24200                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
24201                 [ 'Courier New', 'Courier New'],
24202                 [ 'Tahoma', 'Tahoma'],
24203                 [ 'Times New Roman,serif', 'Times'],
24204                 [ 'Verdana','Verdana' ]
24205         ]
24206 };
24207
24208 // fixme - these need to be configurable..
24209  
24210
24211 //Roo.form.HtmlEditor.ToolbarContext.types
24212
24213
24214 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
24215     
24216     tb: false,
24217     
24218     rendered: false,
24219     
24220     editor : false,
24221     editorcore : false,
24222     /**
24223      * @cfg {Object} disable  List of toolbar elements to disable
24224          
24225      */
24226     disable : false,
24227     /**
24228      * @cfg {Object} styles List of styles 
24229      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
24230      *
24231      * These must be defined in the page, so they get rendered correctly..
24232      * .headline { }
24233      * TD.underline { }
24234      * 
24235      */
24236     styles : false,
24237     
24238     options: false,
24239     
24240     toolbars : false,
24241     
24242     init : function(editor)
24243     {
24244         this.editor = editor;
24245         this.editorcore = editor.editorcore ? editor.editorcore : editor;
24246         var editorcore = this.editorcore;
24247         
24248         var fid = editorcore.frameId;
24249         var etb = this;
24250         function btn(id, toggle, handler){
24251             var xid = fid + '-'+ id ;
24252             return {
24253                 id : xid,
24254                 cmd : id,
24255                 cls : 'x-btn-icon x-edit-'+id,
24256                 enableToggle:toggle !== false,
24257                 scope: editorcore, // was editor...
24258                 handler:handler||editorcore.relayBtnCmd,
24259                 clickEvent:'mousedown',
24260                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
24261                 tabIndex:-1
24262             };
24263         }
24264         // create a new element.
24265         var wdiv = editor.wrap.createChild({
24266                 tag: 'div'
24267             }, editor.wrap.dom.firstChild.nextSibling, true);
24268         
24269         // can we do this more than once??
24270         
24271          // stop form submits
24272       
24273  
24274         // disable everything...
24275         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24276         this.toolbars = {};
24277            
24278         for (var i in  ty) {
24279           
24280             this.toolbars[i] = this.buildToolbar(ty[i],i);
24281         }
24282         this.tb = this.toolbars.BODY;
24283         this.tb.el.show();
24284         this.buildFooter();
24285         this.footer.show();
24286         editor.on('hide', function( ) { this.footer.hide() }, this);
24287         editor.on('show', function( ) { this.footer.show() }, this);
24288         
24289          
24290         this.rendered = true;
24291         
24292         // the all the btns;
24293         editor.on('editorevent', this.updateToolbar, this);
24294         // other toolbars need to implement this..
24295         //editor.on('editmodechange', this.updateToolbar, this);
24296     },
24297     
24298     
24299     
24300     /**
24301      * Protected method that will not generally be called directly. It triggers
24302      * a toolbar update by reading the markup state of the current selection in the editor.
24303      *
24304      * Note you can force an update by calling on('editorevent', scope, false)
24305      */
24306     updateToolbar: function(editor,ev,sel){
24307
24308         //Roo.log(ev);
24309         // capture mouse up - this is handy for selecting images..
24310         // perhaps should go somewhere else...
24311         if(!this.editorcore.activated){
24312              this.editor.onFirstFocus();
24313             return;
24314         }
24315         
24316         
24317         
24318         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
24319         // selectNode - might want to handle IE?
24320         if (ev &&
24321             (ev.type == 'mouseup' || ev.type == 'click' ) &&
24322             ev.target && ev.target.tagName == 'IMG') {
24323             // they have click on an image...
24324             // let's see if we can change the selection...
24325             sel = ev.target;
24326          
24327               var nodeRange = sel.ownerDocument.createRange();
24328             try {
24329                 nodeRange.selectNode(sel);
24330             } catch (e) {
24331                 nodeRange.selectNodeContents(sel);
24332             }
24333             //nodeRange.collapse(true);
24334             var s = this.editorcore.win.getSelection();
24335             s.removeAllRanges();
24336             s.addRange(nodeRange);
24337         }  
24338         
24339       
24340         var updateFooter = sel ? false : true;
24341         
24342         
24343         var ans = this.editorcore.getAllAncestors();
24344         
24345         // pick
24346         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24347         
24348         if (!sel) { 
24349             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24350             sel = sel ? sel : this.editorcore.doc.body;
24351             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24352             
24353         }
24354         // pick a menu that exists..
24355         var tn = sel.tagName.toUpperCase();
24356         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24357         
24358         tn = sel.tagName.toUpperCase();
24359         
24360         var lastSel = this.tb.selectedNode;
24361         
24362         this.tb.selectedNode = sel;
24363         
24364         // if current menu does not match..
24365         
24366         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24367                 
24368             this.tb.el.hide();
24369             ///console.log("show: " + tn);
24370             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24371             this.tb.el.show();
24372             // update name
24373             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24374             
24375             
24376             // update attributes
24377             if (this.tb.fields) {
24378                 this.tb.fields.each(function(e) {
24379                     if (e.stylename) {
24380                         e.setValue(sel.style[e.stylename]);
24381                         return;
24382                     } 
24383                    e.setValue(sel.getAttribute(e.attrname));
24384                 });
24385             }
24386             
24387             var hasStyles = false;
24388             for(var i in this.styles) {
24389                 hasStyles = true;
24390                 break;
24391             }
24392             
24393             // update styles
24394             if (hasStyles) { 
24395                 var st = this.tb.fields.item(0);
24396                 
24397                 st.store.removeAll();
24398                
24399                 
24400                 var cn = sel.className.split(/\s+/);
24401                 
24402                 var avs = [];
24403                 if (this.styles['*']) {
24404                     
24405                     Roo.each(this.styles['*'], function(v) {
24406                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24407                     });
24408                 }
24409                 if (this.styles[tn]) { 
24410                     Roo.each(this.styles[tn], function(v) {
24411                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24412                     });
24413                 }
24414                 
24415                 st.store.loadData(avs);
24416                 st.collapse();
24417                 st.setValue(cn);
24418             }
24419             // flag our selected Node.
24420             this.tb.selectedNode = sel;
24421            
24422            
24423             Roo.menu.MenuMgr.hideAll();
24424
24425         }
24426         
24427         if (!updateFooter) {
24428             //this.footDisp.dom.innerHTML = ''; 
24429             return;
24430         }
24431         // update the footer
24432         //
24433         var html = '';
24434         
24435         this.footerEls = ans.reverse();
24436         Roo.each(this.footerEls, function(a,i) {
24437             if (!a) { return; }
24438             html += html.length ? ' &gt; '  :  '';
24439             
24440             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24441             
24442         });
24443        
24444         // 
24445         var sz = this.footDisp.up('td').getSize();
24446         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24447         this.footDisp.dom.style.marginLeft = '5px';
24448         
24449         this.footDisp.dom.style.overflow = 'hidden';
24450         
24451         this.footDisp.dom.innerHTML = html;
24452             
24453         //this.editorsyncValue();
24454     },
24455      
24456     
24457    
24458        
24459     // private
24460     onDestroy : function(){
24461         if(this.rendered){
24462             
24463             this.tb.items.each(function(item){
24464                 if(item.menu){
24465                     item.menu.removeAll();
24466                     if(item.menu.el){
24467                         item.menu.el.destroy();
24468                     }
24469                 }
24470                 item.destroy();
24471             });
24472              
24473         }
24474     },
24475     onFirstFocus: function() {
24476         // need to do this for all the toolbars..
24477         this.tb.items.each(function(item){
24478            item.enable();
24479         });
24480     },
24481     buildToolbar: function(tlist, nm)
24482     {
24483         var editor = this.editor;
24484         var editorcore = this.editorcore;
24485          // create a new element.
24486         var wdiv = editor.wrap.createChild({
24487                 tag: 'div'
24488             }, editor.wrap.dom.firstChild.nextSibling, true);
24489         
24490        
24491         var tb = new Roo.Toolbar(wdiv);
24492         // add the name..
24493         
24494         tb.add(nm+ ":&nbsp;");
24495         
24496         var styles = [];
24497         for(var i in this.styles) {
24498             styles.push(i);
24499         }
24500         
24501         // styles...
24502         if (styles && styles.length) {
24503             
24504             // this needs a multi-select checkbox...
24505             tb.addField( new Roo.form.ComboBox({
24506                 store: new Roo.data.SimpleStore({
24507                     id : 'val',
24508                     fields: ['val', 'selected'],
24509                     data : [] 
24510                 }),
24511                 name : '-roo-edit-className',
24512                 attrname : 'className',
24513                 displayField: 'val',
24514                 typeAhead: false,
24515                 mode: 'local',
24516                 editable : false,
24517                 triggerAction: 'all',
24518                 emptyText:'Select Style',
24519                 selectOnFocus:true,
24520                 width: 130,
24521                 listeners : {
24522                     'select': function(c, r, i) {
24523                         // initial support only for on class per el..
24524                         tb.selectedNode.className =  r ? r.get('val') : '';
24525                         editorcore.syncValue();
24526                     }
24527                 }
24528     
24529             }));
24530         }
24531         
24532         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24533         var tbops = tbc.options;
24534         
24535         for (var i in tlist) {
24536             
24537             var item = tlist[i];
24538             tb.add(item.title + ":&nbsp;");
24539             
24540             
24541             //optname == used so you can configure the options available..
24542             var opts = item.opts ? item.opts : false;
24543             if (item.optname) {
24544                 opts = tbops[item.optname];
24545            
24546             }
24547             
24548             if (opts) {
24549                 // opts == pulldown..
24550                 tb.addField( new Roo.form.ComboBox({
24551                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24552                         id : 'val',
24553                         fields: ['val', 'display'],
24554                         data : opts  
24555                     }),
24556                     name : '-roo-edit-' + i,
24557                     attrname : i,
24558                     stylename : item.style ? item.style : false,
24559                     displayField: item.displayField ? item.displayField : 'val',
24560                     valueField :  'val',
24561                     typeAhead: false,
24562                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24563                     editable : false,
24564                     triggerAction: 'all',
24565                     emptyText:'Select',
24566                     selectOnFocus:true,
24567                     width: item.width ? item.width  : 130,
24568                     listeners : {
24569                         'select': function(c, r, i) {
24570                             if (c.stylename) {
24571                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24572                                 return;
24573                             }
24574                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24575                         }
24576                     }
24577
24578                 }));
24579                 continue;
24580                     
24581                  
24582                 
24583                 tb.addField( new Roo.form.TextField({
24584                     name: i,
24585                     width: 100,
24586                     //allowBlank:false,
24587                     value: ''
24588                 }));
24589                 continue;
24590             }
24591             tb.addField( new Roo.form.TextField({
24592                 name: '-roo-edit-' + i,
24593                 attrname : i,
24594                 
24595                 width: item.width,
24596                 //allowBlank:true,
24597                 value: '',
24598                 listeners: {
24599                     'change' : function(f, nv, ov) {
24600                         tb.selectedNode.setAttribute(f.attrname, nv);
24601                         editorcore.syncValue();
24602                     }
24603                 }
24604             }));
24605              
24606         }
24607         
24608         var _this = this;
24609         
24610         if(nm == 'BODY'){
24611             tb.addSeparator();
24612         
24613             tb.addButton( {
24614                 text: 'Stylesheets',
24615
24616                 listeners : {
24617                     click : function ()
24618                     {
24619                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24620                     }
24621                 }
24622             });
24623         }
24624         
24625         tb.addFill();
24626         tb.addButton( {
24627             text: 'Remove Tag',
24628     
24629             listeners : {
24630                 click : function ()
24631                 {
24632                     // remove
24633                     // undo does not work.
24634                      
24635                     var sn = tb.selectedNode;
24636                     
24637                     var pn = sn.parentNode;
24638                     
24639                     var stn =  sn.childNodes[0];
24640                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24641                     while (sn.childNodes.length) {
24642                         var node = sn.childNodes[0];
24643                         sn.removeChild(node);
24644                         //Roo.log(node);
24645                         pn.insertBefore(node, sn);
24646                         
24647                     }
24648                     pn.removeChild(sn);
24649                     var range = editorcore.createRange();
24650         
24651                     range.setStart(stn,0);
24652                     range.setEnd(en,0); //????
24653                     //range.selectNode(sel);
24654                     
24655                     
24656                     var selection = editorcore.getSelection();
24657                     selection.removeAllRanges();
24658                     selection.addRange(range);
24659                     
24660                     
24661                     
24662                     //_this.updateToolbar(null, null, pn);
24663                     _this.updateToolbar(null, null, null);
24664                     _this.footDisp.dom.innerHTML = ''; 
24665                 }
24666             }
24667             
24668                     
24669                 
24670             
24671         });
24672         
24673         
24674         tb.el.on('click', function(e){
24675             e.preventDefault(); // what does this do?
24676         });
24677         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24678         tb.el.hide();
24679         tb.name = nm;
24680         // dont need to disable them... as they will get hidden
24681         return tb;
24682          
24683         
24684     },
24685     buildFooter : function()
24686     {
24687         
24688         var fel = this.editor.wrap.createChild();
24689         this.footer = new Roo.Toolbar(fel);
24690         // toolbar has scrolly on left / right?
24691         var footDisp= new Roo.Toolbar.Fill();
24692         var _t = this;
24693         this.footer.add(
24694             {
24695                 text : '&lt;',
24696                 xtype: 'Button',
24697                 handler : function() {
24698                     _t.footDisp.scrollTo('left',0,true)
24699                 }
24700             }
24701         );
24702         this.footer.add( footDisp );
24703         this.footer.add( 
24704             {
24705                 text : '&gt;',
24706                 xtype: 'Button',
24707                 handler : function() {
24708                     // no animation..
24709                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24710                 }
24711             }
24712         );
24713         var fel = Roo.get(footDisp.el);
24714         fel.addClass('x-editor-context');
24715         this.footDispWrap = fel; 
24716         this.footDispWrap.overflow  = 'hidden';
24717         
24718         this.footDisp = fel.createChild();
24719         this.footDispWrap.on('click', this.onContextClick, this)
24720         
24721         
24722     },
24723     onContextClick : function (ev,dom)
24724     {
24725         ev.preventDefault();
24726         var  cn = dom.className;
24727         //Roo.log(cn);
24728         if (!cn.match(/x-ed-loc-/)) {
24729             return;
24730         }
24731         var n = cn.split('-').pop();
24732         var ans = this.footerEls;
24733         var sel = ans[n];
24734         
24735          // pick
24736         var range = this.editorcore.createRange();
24737         
24738         range.selectNodeContents(sel);
24739         //range.selectNode(sel);
24740         
24741         
24742         var selection = this.editorcore.getSelection();
24743         selection.removeAllRanges();
24744         selection.addRange(range);
24745         
24746         
24747         
24748         this.updateToolbar(null, null, sel);
24749         
24750         
24751     }
24752     
24753     
24754     
24755     
24756     
24757 });
24758
24759
24760
24761
24762
24763 /*
24764  * Based on:
24765  * Ext JS Library 1.1.1
24766  * Copyright(c) 2006-2007, Ext JS, LLC.
24767  *
24768  * Originally Released Under LGPL - original licence link has changed is not relivant.
24769  *
24770  * Fork - LGPL
24771  * <script type="text/javascript">
24772  */
24773  
24774 /**
24775  * @class Roo.form.BasicForm
24776  * @extends Roo.util.Observable
24777  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24778  * @constructor
24779  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24780  * @param {Object} config Configuration options
24781  */
24782 Roo.form.BasicForm = function(el, config){
24783     this.allItems = [];
24784     this.childForms = [];
24785     Roo.apply(this, config);
24786     /*
24787      * The Roo.form.Field items in this form.
24788      * @type MixedCollection
24789      */
24790      
24791      
24792     this.items = new Roo.util.MixedCollection(false, function(o){
24793         return o.id || (o.id = Roo.id());
24794     });
24795     this.addEvents({
24796         /**
24797          * @event beforeaction
24798          * Fires before any action is performed. Return false to cancel the action.
24799          * @param {Form} this
24800          * @param {Action} action The action to be performed
24801          */
24802         beforeaction: true,
24803         /**
24804          * @event actionfailed
24805          * Fires when an action fails.
24806          * @param {Form} this
24807          * @param {Action} action The action that failed
24808          */
24809         actionfailed : true,
24810         /**
24811          * @event actioncomplete
24812          * Fires when an action is completed.
24813          * @param {Form} this
24814          * @param {Action} action The action that completed
24815          */
24816         actioncomplete : true
24817     });
24818     if(el){
24819         this.initEl(el);
24820     }
24821     Roo.form.BasicForm.superclass.constructor.call(this);
24822     
24823     Roo.form.BasicForm.popover.apply();
24824 };
24825
24826 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24827     /**
24828      * @cfg {String} method
24829      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24830      */
24831     /**
24832      * @cfg {DataReader} reader
24833      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24834      * This is optional as there is built-in support for processing JSON.
24835      */
24836     /**
24837      * @cfg {DataReader} errorReader
24838      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24839      * This is completely optional as there is built-in support for processing JSON.
24840      */
24841     /**
24842      * @cfg {String} url
24843      * The URL to use for form actions if one isn't supplied in the action options.
24844      */
24845     /**
24846      * @cfg {Boolean} fileUpload
24847      * Set to true if this form is a file upload.
24848      */
24849      
24850     /**
24851      * @cfg {Object} baseParams
24852      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24853      */
24854      /**
24855      
24856     /**
24857      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24858      */
24859     timeout: 30,
24860
24861     // private
24862     activeAction : null,
24863
24864     /**
24865      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24866      * or setValues() data instead of when the form was first created.
24867      */
24868     trackResetOnLoad : false,
24869     
24870     
24871     /**
24872      * childForms - used for multi-tab forms
24873      * @type {Array}
24874      */
24875     childForms : false,
24876     
24877     /**
24878      * allItems - full list of fields.
24879      * @type {Array}
24880      */
24881     allItems : false,
24882     
24883     /**
24884      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24885      * element by passing it or its id or mask the form itself by passing in true.
24886      * @type Mixed
24887      */
24888     waitMsgTarget : false,
24889     
24890     /**
24891      * @type Boolean
24892      */
24893     disableMask : false,
24894     
24895     /**
24896      * @cfg {Boolean} errorMask (true|false) default false
24897      */
24898     errorMask : false,
24899     
24900     /**
24901      * @cfg {Number} maskOffset Default 100
24902      */
24903     maskOffset : 100,
24904
24905     // private
24906     initEl : function(el){
24907         this.el = Roo.get(el);
24908         this.id = this.el.id || Roo.id();
24909         this.el.on('submit', this.onSubmit, this);
24910         this.el.addClass('x-form');
24911     },
24912
24913     // private
24914     onSubmit : function(e){
24915         e.stopEvent();
24916     },
24917
24918     /**
24919      * Returns true if client-side validation on the form is successful.
24920      * @return Boolean
24921      */
24922     isValid : function(){
24923         var valid = true;
24924         var target = false;
24925         this.items.each(function(f){
24926             if(f.validate()){
24927                 return;
24928             }
24929             
24930             valid = false;
24931                 
24932             if(!target && f.el.isVisible(true)){
24933                 target = f;
24934             }
24935         });
24936         
24937         if(this.errorMask && !valid){
24938             Roo.form.BasicForm.popover.mask(this, target);
24939         }
24940         
24941         return valid;
24942     },
24943
24944     /**
24945      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24946      * @return Boolean
24947      */
24948     isDirty : function(){
24949         var dirty = false;
24950         this.items.each(function(f){
24951            if(f.isDirty()){
24952                dirty = true;
24953                return false;
24954            }
24955         });
24956         return dirty;
24957     },
24958     
24959     /**
24960      * Returns true if any fields in this form have changed since their original load. (New version)
24961      * @return Boolean
24962      */
24963     
24964     hasChanged : function()
24965     {
24966         var dirty = false;
24967         this.items.each(function(f){
24968            if(f.hasChanged()){
24969                dirty = true;
24970                return false;
24971            }
24972         });
24973         return dirty;
24974         
24975     },
24976     /**
24977      * Resets all hasChanged to 'false' -
24978      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24979      * So hasChanged storage is only to be used for this purpose
24980      * @return Boolean
24981      */
24982     resetHasChanged : function()
24983     {
24984         this.items.each(function(f){
24985            f.resetHasChanged();
24986         });
24987         
24988     },
24989     
24990     
24991     /**
24992      * Performs a predefined action (submit or load) or custom actions you define on this form.
24993      * @param {String} actionName The name of the action type
24994      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24995      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24996      * accept other config options):
24997      * <pre>
24998 Property          Type             Description
24999 ----------------  ---------------  ----------------------------------------------------------------------------------
25000 url               String           The url for the action (defaults to the form's url)
25001 method            String           The form method to use (defaults to the form's method, or POST if not defined)
25002 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
25003 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
25004                                    validate the form on the client (defaults to false)
25005      * </pre>
25006      * @return {BasicForm} this
25007      */
25008     doAction : function(action, options){
25009         if(typeof action == 'string'){
25010             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
25011         }
25012         if(this.fireEvent('beforeaction', this, action) !== false){
25013             this.beforeAction(action);
25014             action.run.defer(100, action);
25015         }
25016         return this;
25017     },
25018
25019     /**
25020      * Shortcut to do a submit action.
25021      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25022      * @return {BasicForm} this
25023      */
25024     submit : function(options){
25025         this.doAction('submit', options);
25026         return this;
25027     },
25028
25029     /**
25030      * Shortcut to do a load action.
25031      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
25032      * @return {BasicForm} this
25033      */
25034     load : function(options){
25035         this.doAction('load', options);
25036         return this;
25037     },
25038
25039     /**
25040      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
25041      * @param {Record} record The record to edit
25042      * @return {BasicForm} this
25043      */
25044     updateRecord : function(record){
25045         record.beginEdit();
25046         var fs = record.fields;
25047         fs.each(function(f){
25048             var field = this.findField(f.name);
25049             if(field){
25050                 record.set(f.name, field.getValue());
25051             }
25052         }, this);
25053         record.endEdit();
25054         return this;
25055     },
25056
25057     /**
25058      * Loads an Roo.data.Record into this form.
25059      * @param {Record} record The record to load
25060      * @return {BasicForm} this
25061      */
25062     loadRecord : function(record){
25063         this.setValues(record.data);
25064         return this;
25065     },
25066
25067     // private
25068     beforeAction : function(action){
25069         var o = action.options;
25070         
25071         if(!this.disableMask) {
25072             if(this.waitMsgTarget === true){
25073                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
25074             }else if(this.waitMsgTarget){
25075                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
25076                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
25077             }else {
25078                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
25079             }
25080         }
25081         
25082          
25083     },
25084
25085     // private
25086     afterAction : function(action, success){
25087         this.activeAction = null;
25088         var o = action.options;
25089         
25090         if(!this.disableMask) {
25091             if(this.waitMsgTarget === true){
25092                 this.el.unmask();
25093             }else if(this.waitMsgTarget){
25094                 this.waitMsgTarget.unmask();
25095             }else{
25096                 Roo.MessageBox.updateProgress(1);
25097                 Roo.MessageBox.hide();
25098             }
25099         }
25100         
25101         if(success){
25102             if(o.reset){
25103                 this.reset();
25104             }
25105             Roo.callback(o.success, o.scope, [this, action]);
25106             this.fireEvent('actioncomplete', this, action);
25107             
25108         }else{
25109             
25110             // failure condition..
25111             // we have a scenario where updates need confirming.
25112             // eg. if a locking scenario exists..
25113             // we look for { errors : { needs_confirm : true }} in the response.
25114             if (
25115                 (typeof(action.result) != 'undefined')  &&
25116                 (typeof(action.result.errors) != 'undefined')  &&
25117                 (typeof(action.result.errors.needs_confirm) != 'undefined')
25118            ){
25119                 var _t = this;
25120                 Roo.MessageBox.confirm(
25121                     "Change requires confirmation",
25122                     action.result.errorMsg,
25123                     function(r) {
25124                         if (r != 'yes') {
25125                             return;
25126                         }
25127                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
25128                     }
25129                     
25130                 );
25131                 
25132                 
25133                 
25134                 return;
25135             }
25136             
25137             Roo.callback(o.failure, o.scope, [this, action]);
25138             // show an error message if no failed handler is set..
25139             if (!this.hasListener('actionfailed')) {
25140                 Roo.MessageBox.alert("Error",
25141                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
25142                         action.result.errorMsg :
25143                         "Saving Failed, please check your entries or try again"
25144                 );
25145             }
25146             
25147             this.fireEvent('actionfailed', this, action);
25148         }
25149         
25150     },
25151
25152     /**
25153      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
25154      * @param {String} id The value to search for
25155      * @return Field
25156      */
25157     findField : function(id){
25158         var field = this.items.get(id);
25159         if(!field){
25160             this.items.each(function(f){
25161                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
25162                     field = f;
25163                     return false;
25164                 }
25165             });
25166         }
25167         return field || null;
25168     },
25169
25170     /**
25171      * Add a secondary form to this one, 
25172      * Used to provide tabbed forms. One form is primary, with hidden values 
25173      * which mirror the elements from the other forms.
25174      * 
25175      * @param {Roo.form.Form} form to add.
25176      * 
25177      */
25178     addForm : function(form)
25179     {
25180        
25181         if (this.childForms.indexOf(form) > -1) {
25182             // already added..
25183             return;
25184         }
25185         this.childForms.push(form);
25186         var n = '';
25187         Roo.each(form.allItems, function (fe) {
25188             
25189             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
25190             if (this.findField(n)) { // already added..
25191                 return;
25192             }
25193             var add = new Roo.form.Hidden({
25194                 name : n
25195             });
25196             add.render(this.el);
25197             
25198             this.add( add );
25199         }, this);
25200         
25201     },
25202     /**
25203      * Mark fields in this form invalid in bulk.
25204      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
25205      * @return {BasicForm} this
25206      */
25207     markInvalid : function(errors){
25208         if(errors instanceof Array){
25209             for(var i = 0, len = errors.length; i < len; i++){
25210                 var fieldError = errors[i];
25211                 var f = this.findField(fieldError.id);
25212                 if(f){
25213                     f.markInvalid(fieldError.msg);
25214                 }
25215             }
25216         }else{
25217             var field, id;
25218             for(id in errors){
25219                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
25220                     field.markInvalid(errors[id]);
25221                 }
25222             }
25223         }
25224         Roo.each(this.childForms || [], function (f) {
25225             f.markInvalid(errors);
25226         });
25227         
25228         return this;
25229     },
25230
25231     /**
25232      * Set values for fields in this form in bulk.
25233      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
25234      * @return {BasicForm} this
25235      */
25236     setValues : function(values){
25237         if(values instanceof Array){ // array of objects
25238             for(var i = 0, len = values.length; i < len; i++){
25239                 var v = values[i];
25240                 var f = this.findField(v.id);
25241                 if(f){
25242                     f.setValue(v.value);
25243                     if(this.trackResetOnLoad){
25244                         f.originalValue = f.getValue();
25245                     }
25246                 }
25247             }
25248         }else{ // object hash
25249             var field, id;
25250             for(id in values){
25251                 if(typeof values[id] != 'function' && (field = this.findField(id))){
25252                     
25253                     if (field.setFromData && 
25254                         field.valueField && 
25255                         field.displayField &&
25256                         // combos' with local stores can 
25257                         // be queried via setValue()
25258                         // to set their value..
25259                         (field.store && !field.store.isLocal)
25260                         ) {
25261                         // it's a combo
25262                         var sd = { };
25263                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
25264                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
25265                         field.setFromData(sd);
25266                         
25267                     } else {
25268                         field.setValue(values[id]);
25269                     }
25270                     
25271                     
25272                     if(this.trackResetOnLoad){
25273                         field.originalValue = field.getValue();
25274                     }
25275                 }
25276             }
25277         }
25278         this.resetHasChanged();
25279         
25280         
25281         Roo.each(this.childForms || [], function (f) {
25282             f.setValues(values);
25283             f.resetHasChanged();
25284         });
25285                 
25286         return this;
25287     },
25288  
25289     /**
25290      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
25291      * they are returned as an array.
25292      * @param {Boolean} asString
25293      * @return {Object}
25294      */
25295     getValues : function(asString){
25296         if (this.childForms) {
25297             // copy values from the child forms
25298             Roo.each(this.childForms, function (f) {
25299                 this.setValues(f.getValues());
25300             }, this);
25301         }
25302         
25303         // use formdata
25304         if (typeof(FormData) != 'undefined' && asString !== true) {
25305             var fd = (new FormData(this.el.dom)).entries();
25306             var ret = {};
25307             var ent = fd.next();
25308             while (!ent.done) {
25309                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
25310                 ent = fd.next();
25311             };
25312             return ret;
25313         }
25314         
25315         
25316         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
25317         if(asString === true){
25318             return fs;
25319         }
25320         return Roo.urlDecode(fs);
25321     },
25322     
25323     /**
25324      * Returns the fields in this form as an object with key/value pairs. 
25325      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
25326      * @return {Object}
25327      */
25328     getFieldValues : function(with_hidden)
25329     {
25330         if (this.childForms) {
25331             // copy values from the child forms
25332             // should this call getFieldValues - probably not as we do not currently copy
25333             // hidden fields when we generate..
25334             Roo.each(this.childForms, function (f) {
25335                 this.setValues(f.getValues());
25336             }, this);
25337         }
25338         
25339         var ret = {};
25340         this.items.each(function(f){
25341             if (!f.getName()) {
25342                 return;
25343             }
25344             var v = f.getValue();
25345             if (f.inputType =='radio') {
25346                 if (typeof(ret[f.getName()]) == 'undefined') {
25347                     ret[f.getName()] = ''; // empty..
25348                 }
25349                 
25350                 if (!f.el.dom.checked) {
25351                     return;
25352                     
25353                 }
25354                 v = f.el.dom.value;
25355                 
25356             }
25357             
25358             // not sure if this supported any more..
25359             if ((typeof(v) == 'object') && f.getRawValue) {
25360                 v = f.getRawValue() ; // dates..
25361             }
25362             // combo boxes where name != hiddenName...
25363             if (f.name != f.getName()) {
25364                 ret[f.name] = f.getRawValue();
25365             }
25366             ret[f.getName()] = v;
25367         });
25368         
25369         return ret;
25370     },
25371
25372     /**
25373      * Clears all invalid messages in this form.
25374      * @return {BasicForm} this
25375      */
25376     clearInvalid : function(){
25377         this.items.each(function(f){
25378            f.clearInvalid();
25379         });
25380         
25381         Roo.each(this.childForms || [], function (f) {
25382             f.clearInvalid();
25383         });
25384         
25385         
25386         return this;
25387     },
25388
25389     /**
25390      * Resets this form.
25391      * @return {BasicForm} this
25392      */
25393     reset : function(){
25394         this.items.each(function(f){
25395             f.reset();
25396         });
25397         
25398         Roo.each(this.childForms || [], function (f) {
25399             f.reset();
25400         });
25401         this.resetHasChanged();
25402         
25403         return this;
25404     },
25405
25406     /**
25407      * Add Roo.form components to this form.
25408      * @param {Field} field1
25409      * @param {Field} field2 (optional)
25410      * @param {Field} etc (optional)
25411      * @return {BasicForm} this
25412      */
25413     add : function(){
25414         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25415         return this;
25416     },
25417
25418
25419     /**
25420      * Removes a field from the items collection (does NOT remove its markup).
25421      * @param {Field} field
25422      * @return {BasicForm} this
25423      */
25424     remove : function(field){
25425         this.items.remove(field);
25426         return this;
25427     },
25428
25429     /**
25430      * Looks at the fields in this form, checks them for an id attribute,
25431      * and calls applyTo on the existing dom element with that id.
25432      * @return {BasicForm} this
25433      */
25434     render : function(){
25435         this.items.each(function(f){
25436             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25437                 f.applyTo(f.id);
25438             }
25439         });
25440         return this;
25441     },
25442
25443     /**
25444      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25445      * @param {Object} values
25446      * @return {BasicForm} this
25447      */
25448     applyToFields : function(o){
25449         this.items.each(function(f){
25450            Roo.apply(f, o);
25451         });
25452         return this;
25453     },
25454
25455     /**
25456      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25457      * @param {Object} values
25458      * @return {BasicForm} this
25459      */
25460     applyIfToFields : function(o){
25461         this.items.each(function(f){
25462            Roo.applyIf(f, o);
25463         });
25464         return this;
25465     }
25466 });
25467
25468 // back compat
25469 Roo.BasicForm = Roo.form.BasicForm;
25470
25471 Roo.apply(Roo.form.BasicForm, {
25472     
25473     popover : {
25474         
25475         padding : 5,
25476         
25477         isApplied : false,
25478         
25479         isMasked : false,
25480         
25481         form : false,
25482         
25483         target : false,
25484         
25485         intervalID : false,
25486         
25487         maskEl : false,
25488         
25489         apply : function()
25490         {
25491             if(this.isApplied){
25492                 return;
25493             }
25494             
25495             this.maskEl = {
25496                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25497                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25498                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25499                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25500             };
25501             
25502             this.maskEl.top.enableDisplayMode("block");
25503             this.maskEl.left.enableDisplayMode("block");
25504             this.maskEl.bottom.enableDisplayMode("block");
25505             this.maskEl.right.enableDisplayMode("block");
25506             
25507             Roo.get(document.body).on('click', function(){
25508                 this.unmask();
25509             }, this);
25510             
25511             Roo.get(document.body).on('touchstart', function(){
25512                 this.unmask();
25513             }, this);
25514             
25515             this.isApplied = true
25516         },
25517         
25518         mask : function(form, target)
25519         {
25520             this.form = form;
25521             
25522             this.target = target;
25523             
25524             if(!this.form.errorMask || !target.el){
25525                 return;
25526             }
25527             
25528             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25529             
25530             var ot = this.target.el.calcOffsetsTo(scrollable);
25531             
25532             var scrollTo = ot[1] - this.form.maskOffset;
25533             
25534             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25535             
25536             scrollable.scrollTo('top', scrollTo);
25537             
25538             var el = this.target.wrap || this.target.el;
25539             
25540             var box = el.getBox();
25541             
25542             this.maskEl.top.setStyle('position', 'absolute');
25543             this.maskEl.top.setStyle('z-index', 10000);
25544             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25545             this.maskEl.top.setLeft(0);
25546             this.maskEl.top.setTop(0);
25547             this.maskEl.top.show();
25548             
25549             this.maskEl.left.setStyle('position', 'absolute');
25550             this.maskEl.left.setStyle('z-index', 10000);
25551             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25552             this.maskEl.left.setLeft(0);
25553             this.maskEl.left.setTop(box.y - this.padding);
25554             this.maskEl.left.show();
25555
25556             this.maskEl.bottom.setStyle('position', 'absolute');
25557             this.maskEl.bottom.setStyle('z-index', 10000);
25558             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25559             this.maskEl.bottom.setLeft(0);
25560             this.maskEl.bottom.setTop(box.bottom + this.padding);
25561             this.maskEl.bottom.show();
25562
25563             this.maskEl.right.setStyle('position', 'absolute');
25564             this.maskEl.right.setStyle('z-index', 10000);
25565             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25566             this.maskEl.right.setLeft(box.right + this.padding);
25567             this.maskEl.right.setTop(box.y - this.padding);
25568             this.maskEl.right.show();
25569
25570             this.intervalID = window.setInterval(function() {
25571                 Roo.form.BasicForm.popover.unmask();
25572             }, 10000);
25573
25574             window.onwheel = function(){ return false;};
25575             
25576             (function(){ this.isMasked = true; }).defer(500, this);
25577             
25578         },
25579         
25580         unmask : function()
25581         {
25582             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25583                 return;
25584             }
25585             
25586             this.maskEl.top.setStyle('position', 'absolute');
25587             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25588             this.maskEl.top.hide();
25589
25590             this.maskEl.left.setStyle('position', 'absolute');
25591             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25592             this.maskEl.left.hide();
25593
25594             this.maskEl.bottom.setStyle('position', 'absolute');
25595             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25596             this.maskEl.bottom.hide();
25597
25598             this.maskEl.right.setStyle('position', 'absolute');
25599             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25600             this.maskEl.right.hide();
25601             
25602             window.onwheel = function(){ return true;};
25603             
25604             if(this.intervalID){
25605                 window.clearInterval(this.intervalID);
25606                 this.intervalID = false;
25607             }
25608             
25609             this.isMasked = false;
25610             
25611         }
25612         
25613     }
25614     
25615 });/*
25616  * Based on:
25617  * Ext JS Library 1.1.1
25618  * Copyright(c) 2006-2007, Ext JS, LLC.
25619  *
25620  * Originally Released Under LGPL - original licence link has changed is not relivant.
25621  *
25622  * Fork - LGPL
25623  * <script type="text/javascript">
25624  */
25625
25626 /**
25627  * @class Roo.form.Form
25628  * @extends Roo.form.BasicForm
25629  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25630  * @constructor
25631  * @param {Object} config Configuration options
25632  */
25633 Roo.form.Form = function(config){
25634     var xitems =  [];
25635     if (config.items) {
25636         xitems = config.items;
25637         delete config.items;
25638     }
25639    
25640     
25641     Roo.form.Form.superclass.constructor.call(this, null, config);
25642     this.url = this.url || this.action;
25643     if(!this.root){
25644         this.root = new Roo.form.Layout(Roo.applyIf({
25645             id: Roo.id()
25646         }, config));
25647     }
25648     this.active = this.root;
25649     /**
25650      * Array of all the buttons that have been added to this form via {@link addButton}
25651      * @type Array
25652      */
25653     this.buttons = [];
25654     this.allItems = [];
25655     this.addEvents({
25656         /**
25657          * @event clientvalidation
25658          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25659          * @param {Form} this
25660          * @param {Boolean} valid true if the form has passed client-side validation
25661          */
25662         clientvalidation: true,
25663         /**
25664          * @event rendered
25665          * Fires when the form is rendered
25666          * @param {Roo.form.Form} form
25667          */
25668         rendered : true
25669     });
25670     
25671     if (this.progressUrl) {
25672             // push a hidden field onto the list of fields..
25673             this.addxtype( {
25674                     xns: Roo.form, 
25675                     xtype : 'Hidden', 
25676                     name : 'UPLOAD_IDENTIFIER' 
25677             });
25678         }
25679         
25680     
25681     Roo.each(xitems, this.addxtype, this);
25682     
25683 };
25684
25685 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25686     /**
25687      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25688      */
25689     /**
25690      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25691      */
25692     /**
25693      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25694      */
25695     buttonAlign:'center',
25696
25697     /**
25698      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25699      */
25700     minButtonWidth:75,
25701
25702     /**
25703      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25704      * This property cascades to child containers if not set.
25705      */
25706     labelAlign:'left',
25707
25708     /**
25709      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25710      * fires a looping event with that state. This is required to bind buttons to the valid
25711      * state using the config value formBind:true on the button.
25712      */
25713     monitorValid : false,
25714
25715     /**
25716      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25717      */
25718     monitorPoll : 200,
25719     
25720     /**
25721      * @cfg {String} progressUrl - Url to return progress data 
25722      */
25723     
25724     progressUrl : false,
25725     /**
25726      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25727      * sending a formdata with extra parameters - eg uploaded elements.
25728      */
25729     
25730     formData : false,
25731     
25732     /**
25733      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25734      * fields are added and the column is closed. If no fields are passed the column remains open
25735      * until end() is called.
25736      * @param {Object} config The config to pass to the column
25737      * @param {Field} field1 (optional)
25738      * @param {Field} field2 (optional)
25739      * @param {Field} etc (optional)
25740      * @return Column The column container object
25741      */
25742     column : function(c){
25743         var col = new Roo.form.Column(c);
25744         this.start(col);
25745         if(arguments.length > 1){ // duplicate code required because of Opera
25746             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25747             this.end();
25748         }
25749         return col;
25750     },
25751
25752     /**
25753      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25754      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25755      * until end() is called.
25756      * @param {Object} config The config to pass to the fieldset
25757      * @param {Field} field1 (optional)
25758      * @param {Field} field2 (optional)
25759      * @param {Field} etc (optional)
25760      * @return FieldSet The fieldset container object
25761      */
25762     fieldset : function(c){
25763         var fs = new Roo.form.FieldSet(c);
25764         this.start(fs);
25765         if(arguments.length > 1){ // duplicate code required because of Opera
25766             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25767             this.end();
25768         }
25769         return fs;
25770     },
25771
25772     /**
25773      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25774      * fields are added and the container is closed. If no fields are passed the container remains open
25775      * until end() is called.
25776      * @param {Object} config The config to pass to the Layout
25777      * @param {Field} field1 (optional)
25778      * @param {Field} field2 (optional)
25779      * @param {Field} etc (optional)
25780      * @return Layout The container object
25781      */
25782     container : function(c){
25783         var l = new Roo.form.Layout(c);
25784         this.start(l);
25785         if(arguments.length > 1){ // duplicate code required because of Opera
25786             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25787             this.end();
25788         }
25789         return l;
25790     },
25791
25792     /**
25793      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25794      * @param {Object} container A Roo.form.Layout or subclass of Layout
25795      * @return {Form} this
25796      */
25797     start : function(c){
25798         // cascade label info
25799         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25800         this.active.stack.push(c);
25801         c.ownerCt = this.active;
25802         this.active = c;
25803         return this;
25804     },
25805
25806     /**
25807      * Closes the current open container
25808      * @return {Form} this
25809      */
25810     end : function(){
25811         if(this.active == this.root){
25812             return this;
25813         }
25814         this.active = this.active.ownerCt;
25815         return this;
25816     },
25817
25818     /**
25819      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25820      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25821      * as the label of the field.
25822      * @param {Field} field1
25823      * @param {Field} field2 (optional)
25824      * @param {Field} etc. (optional)
25825      * @return {Form} this
25826      */
25827     add : function(){
25828         this.active.stack.push.apply(this.active.stack, arguments);
25829         this.allItems.push.apply(this.allItems,arguments);
25830         var r = [];
25831         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25832             if(a[i].isFormField){
25833                 r.push(a[i]);
25834             }
25835         }
25836         if(r.length > 0){
25837             Roo.form.Form.superclass.add.apply(this, r);
25838         }
25839         return this;
25840     },
25841     
25842
25843     
25844     
25845     
25846      /**
25847      * Find any element that has been added to a form, using it's ID or name
25848      * This can include framesets, columns etc. along with regular fields..
25849      * @param {String} id - id or name to find.
25850      
25851      * @return {Element} e - or false if nothing found.
25852      */
25853     findbyId : function(id)
25854     {
25855         var ret = false;
25856         if (!id) {
25857             return ret;
25858         }
25859         Roo.each(this.allItems, function(f){
25860             if (f.id == id || f.name == id ){
25861                 ret = f;
25862                 return false;
25863             }
25864         });
25865         return ret;
25866     },
25867
25868     
25869     
25870     /**
25871      * Render this form into the passed container. This should only be called once!
25872      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25873      * @return {Form} this
25874      */
25875     render : function(ct)
25876     {
25877         
25878         
25879         
25880         ct = Roo.get(ct);
25881         var o = this.autoCreate || {
25882             tag: 'form',
25883             method : this.method || 'POST',
25884             id : this.id || Roo.id()
25885         };
25886         this.initEl(ct.createChild(o));
25887
25888         this.root.render(this.el);
25889         
25890        
25891              
25892         this.items.each(function(f){
25893             f.render('x-form-el-'+f.id);
25894         });
25895
25896         if(this.buttons.length > 0){
25897             // tables are required to maintain order and for correct IE layout
25898             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25899                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25900                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25901             }}, null, true);
25902             var tr = tb.getElementsByTagName('tr')[0];
25903             for(var i = 0, len = this.buttons.length; i < len; i++) {
25904                 var b = this.buttons[i];
25905                 var td = document.createElement('td');
25906                 td.className = 'x-form-btn-td';
25907                 b.render(tr.appendChild(td));
25908             }
25909         }
25910         if(this.monitorValid){ // initialize after render
25911             this.startMonitoring();
25912         }
25913         this.fireEvent('rendered', this);
25914         return this;
25915     },
25916
25917     /**
25918      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25919      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25920      * object or a valid Roo.DomHelper element config
25921      * @param {Function} handler The function called when the button is clicked
25922      * @param {Object} scope (optional) The scope of the handler function
25923      * @return {Roo.Button}
25924      */
25925     addButton : function(config, handler, scope){
25926         var bc = {
25927             handler: handler,
25928             scope: scope,
25929             minWidth: this.minButtonWidth,
25930             hideParent:true
25931         };
25932         if(typeof config == "string"){
25933             bc.text = config;
25934         }else{
25935             Roo.apply(bc, config);
25936         }
25937         var btn = new Roo.Button(null, bc);
25938         this.buttons.push(btn);
25939         return btn;
25940     },
25941
25942      /**
25943      * Adds a series of form elements (using the xtype property as the factory method.
25944      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25945      * @param {Object} config 
25946      */
25947     
25948     addxtype : function()
25949     {
25950         var ar = Array.prototype.slice.call(arguments, 0);
25951         var ret = false;
25952         for(var i = 0; i < ar.length; i++) {
25953             if (!ar[i]) {
25954                 continue; // skip -- if this happends something invalid got sent, we 
25955                 // should ignore it, as basically that interface element will not show up
25956                 // and that should be pretty obvious!!
25957             }
25958             
25959             if (Roo.form[ar[i].xtype]) {
25960                 ar[i].form = this;
25961                 var fe = Roo.factory(ar[i], Roo.form);
25962                 if (!ret) {
25963                     ret = fe;
25964                 }
25965                 fe.form = this;
25966                 if (fe.store) {
25967                     fe.store.form = this;
25968                 }
25969                 if (fe.isLayout) {  
25970                          
25971                     this.start(fe);
25972                     this.allItems.push(fe);
25973                     if (fe.items && fe.addxtype) {
25974                         fe.addxtype.apply(fe, fe.items);
25975                         delete fe.items;
25976                     }
25977                      this.end();
25978                     continue;
25979                 }
25980                 
25981                 
25982                  
25983                 this.add(fe);
25984               //  console.log('adding ' + ar[i].xtype);
25985             }
25986             if (ar[i].xtype == 'Button') {  
25987                 //console.log('adding button');
25988                 //console.log(ar[i]);
25989                 this.addButton(ar[i]);
25990                 this.allItems.push(fe);
25991                 continue;
25992             }
25993             
25994             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25995                 alert('end is not supported on xtype any more, use items');
25996             //    this.end();
25997             //    //console.log('adding end');
25998             }
25999             
26000         }
26001         return ret;
26002     },
26003     
26004     /**
26005      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
26006      * option "monitorValid"
26007      */
26008     startMonitoring : function(){
26009         if(!this.bound){
26010             this.bound = true;
26011             Roo.TaskMgr.start({
26012                 run : this.bindHandler,
26013                 interval : this.monitorPoll || 200,
26014                 scope: this
26015             });
26016         }
26017     },
26018
26019     /**
26020      * Stops monitoring of the valid state of this form
26021      */
26022     stopMonitoring : function(){
26023         this.bound = false;
26024     },
26025
26026     // private
26027     bindHandler : function(){
26028         if(!this.bound){
26029             return false; // stops binding
26030         }
26031         var valid = true;
26032         this.items.each(function(f){
26033             if(!f.isValid(true)){
26034                 valid = false;
26035                 return false;
26036             }
26037         });
26038         for(var i = 0, len = this.buttons.length; i < len; i++){
26039             var btn = this.buttons[i];
26040             if(btn.formBind === true && btn.disabled === valid){
26041                 btn.setDisabled(!valid);
26042             }
26043         }
26044         this.fireEvent('clientvalidation', this, valid);
26045     }
26046     
26047     
26048     
26049     
26050     
26051     
26052     
26053     
26054 });
26055
26056
26057 // back compat
26058 Roo.Form = Roo.form.Form;
26059 /*
26060  * Based on:
26061  * Ext JS Library 1.1.1
26062  * Copyright(c) 2006-2007, Ext JS, LLC.
26063  *
26064  * Originally Released Under LGPL - original licence link has changed is not relivant.
26065  *
26066  * Fork - LGPL
26067  * <script type="text/javascript">
26068  */
26069
26070 // as we use this in bootstrap.
26071 Roo.namespace('Roo.form');
26072  /**
26073  * @class Roo.form.Action
26074  * Internal Class used to handle form actions
26075  * @constructor
26076  * @param {Roo.form.BasicForm} el The form element or its id
26077  * @param {Object} config Configuration options
26078  */
26079
26080  
26081  
26082 // define the action interface
26083 Roo.form.Action = function(form, options){
26084     this.form = form;
26085     this.options = options || {};
26086 };
26087 /**
26088  * Client Validation Failed
26089  * @const 
26090  */
26091 Roo.form.Action.CLIENT_INVALID = 'client';
26092 /**
26093  * Server Validation Failed
26094  * @const 
26095  */
26096 Roo.form.Action.SERVER_INVALID = 'server';
26097  /**
26098  * Connect to Server Failed
26099  * @const 
26100  */
26101 Roo.form.Action.CONNECT_FAILURE = 'connect';
26102 /**
26103  * Reading Data from Server Failed
26104  * @const 
26105  */
26106 Roo.form.Action.LOAD_FAILURE = 'load';
26107
26108 Roo.form.Action.prototype = {
26109     type : 'default',
26110     failureType : undefined,
26111     response : undefined,
26112     result : undefined,
26113
26114     // interface method
26115     run : function(options){
26116
26117     },
26118
26119     // interface method
26120     success : function(response){
26121
26122     },
26123
26124     // interface method
26125     handleResponse : function(response){
26126
26127     },
26128
26129     // default connection failure
26130     failure : function(response){
26131         
26132         this.response = response;
26133         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26134         this.form.afterAction(this, false);
26135     },
26136
26137     processResponse : function(response){
26138         this.response = response;
26139         if(!response.responseText){
26140             return true;
26141         }
26142         this.result = this.handleResponse(response);
26143         return this.result;
26144     },
26145
26146     // utility functions used internally
26147     getUrl : function(appendParams){
26148         var url = this.options.url || this.form.url || this.form.el.dom.action;
26149         if(appendParams){
26150             var p = this.getParams();
26151             if(p){
26152                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
26153             }
26154         }
26155         return url;
26156     },
26157
26158     getMethod : function(){
26159         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
26160     },
26161
26162     getParams : function(){
26163         var bp = this.form.baseParams;
26164         var p = this.options.params;
26165         if(p){
26166             if(typeof p == "object"){
26167                 p = Roo.urlEncode(Roo.applyIf(p, bp));
26168             }else if(typeof p == 'string' && bp){
26169                 p += '&' + Roo.urlEncode(bp);
26170             }
26171         }else if(bp){
26172             p = Roo.urlEncode(bp);
26173         }
26174         return p;
26175     },
26176
26177     createCallback : function(){
26178         return {
26179             success: this.success,
26180             failure: this.failure,
26181             scope: this,
26182             timeout: (this.form.timeout*1000),
26183             upload: this.form.fileUpload ? this.success : undefined
26184         };
26185     }
26186 };
26187
26188 Roo.form.Action.Submit = function(form, options){
26189     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
26190 };
26191
26192 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
26193     type : 'submit',
26194
26195     haveProgress : false,
26196     uploadComplete : false,
26197     
26198     // uploadProgress indicator.
26199     uploadProgress : function()
26200     {
26201         if (!this.form.progressUrl) {
26202             return;
26203         }
26204         
26205         if (!this.haveProgress) {
26206             Roo.MessageBox.progress("Uploading", "Uploading");
26207         }
26208         if (this.uploadComplete) {
26209            Roo.MessageBox.hide();
26210            return;
26211         }
26212         
26213         this.haveProgress = true;
26214    
26215         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
26216         
26217         var c = new Roo.data.Connection();
26218         c.request({
26219             url : this.form.progressUrl,
26220             params: {
26221                 id : uid
26222             },
26223             method: 'GET',
26224             success : function(req){
26225                //console.log(data);
26226                 var rdata = false;
26227                 var edata;
26228                 try  {
26229                    rdata = Roo.decode(req.responseText)
26230                 } catch (e) {
26231                     Roo.log("Invalid data from server..");
26232                     Roo.log(edata);
26233                     return;
26234                 }
26235                 if (!rdata || !rdata.success) {
26236                     Roo.log(rdata);
26237                     Roo.MessageBox.alert(Roo.encode(rdata));
26238                     return;
26239                 }
26240                 var data = rdata.data;
26241                 
26242                 if (this.uploadComplete) {
26243                    Roo.MessageBox.hide();
26244                    return;
26245                 }
26246                    
26247                 if (data){
26248                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
26249                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
26250                     );
26251                 }
26252                 this.uploadProgress.defer(2000,this);
26253             },
26254        
26255             failure: function(data) {
26256                 Roo.log('progress url failed ');
26257                 Roo.log(data);
26258             },
26259             scope : this
26260         });
26261            
26262     },
26263     
26264     
26265     run : function()
26266     {
26267         // run get Values on the form, so it syncs any secondary forms.
26268         this.form.getValues();
26269         
26270         var o = this.options;
26271         var method = this.getMethod();
26272         var isPost = method == 'POST';
26273         if(o.clientValidation === false || this.form.isValid()){
26274             
26275             if (this.form.progressUrl) {
26276                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
26277                     (new Date() * 1) + '' + Math.random());
26278                     
26279             } 
26280             
26281             
26282             Roo.Ajax.request(Roo.apply(this.createCallback(), {
26283                 form:this.form.el.dom,
26284                 url:this.getUrl(!isPost),
26285                 method: method,
26286                 params:isPost ? this.getParams() : null,
26287                 isUpload: this.form.fileUpload,
26288                 formData : this.form.formData
26289             }));
26290             
26291             this.uploadProgress();
26292
26293         }else if (o.clientValidation !== false){ // client validation failed
26294             this.failureType = Roo.form.Action.CLIENT_INVALID;
26295             this.form.afterAction(this, false);
26296         }
26297     },
26298
26299     success : function(response)
26300     {
26301         this.uploadComplete= true;
26302         if (this.haveProgress) {
26303             Roo.MessageBox.hide();
26304         }
26305         
26306         
26307         var result = this.processResponse(response);
26308         if(result === true || result.success){
26309             this.form.afterAction(this, true);
26310             return;
26311         }
26312         if(result.errors){
26313             this.form.markInvalid(result.errors);
26314             this.failureType = Roo.form.Action.SERVER_INVALID;
26315         }
26316         this.form.afterAction(this, false);
26317     },
26318     failure : function(response)
26319     {
26320         this.uploadComplete= true;
26321         if (this.haveProgress) {
26322             Roo.MessageBox.hide();
26323         }
26324         
26325         this.response = response;
26326         this.failureType = Roo.form.Action.CONNECT_FAILURE;
26327         this.form.afterAction(this, false);
26328     },
26329     
26330     handleResponse : function(response){
26331         if(this.form.errorReader){
26332             var rs = this.form.errorReader.read(response);
26333             var errors = [];
26334             if(rs.records){
26335                 for(var i = 0, len = rs.records.length; i < len; i++) {
26336                     var r = rs.records[i];
26337                     errors[i] = r.data;
26338                 }
26339             }
26340             if(errors.length < 1){
26341                 errors = null;
26342             }
26343             return {
26344                 success : rs.success,
26345                 errors : errors
26346             };
26347         }
26348         var ret = false;
26349         try {
26350             ret = Roo.decode(response.responseText);
26351         } catch (e) {
26352             ret = {
26353                 success: false,
26354                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26355                 errors : []
26356             };
26357         }
26358         return ret;
26359         
26360     }
26361 });
26362
26363
26364 Roo.form.Action.Load = function(form, options){
26365     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26366     this.reader = this.form.reader;
26367 };
26368
26369 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26370     type : 'load',
26371
26372     run : function(){
26373         
26374         Roo.Ajax.request(Roo.apply(
26375                 this.createCallback(), {
26376                     method:this.getMethod(),
26377                     url:this.getUrl(false),
26378                     params:this.getParams()
26379         }));
26380     },
26381
26382     success : function(response){
26383         
26384         var result = this.processResponse(response);
26385         if(result === true || !result.success || !result.data){
26386             this.failureType = Roo.form.Action.LOAD_FAILURE;
26387             this.form.afterAction(this, false);
26388             return;
26389         }
26390         this.form.clearInvalid();
26391         this.form.setValues(result.data);
26392         this.form.afterAction(this, true);
26393     },
26394
26395     handleResponse : function(response){
26396         if(this.form.reader){
26397             var rs = this.form.reader.read(response);
26398             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26399             return {
26400                 success : rs.success,
26401                 data : data
26402             };
26403         }
26404         return Roo.decode(response.responseText);
26405     }
26406 });
26407
26408 Roo.form.Action.ACTION_TYPES = {
26409     'load' : Roo.form.Action.Load,
26410     'submit' : Roo.form.Action.Submit
26411 };/*
26412  * Based on:
26413  * Ext JS Library 1.1.1
26414  * Copyright(c) 2006-2007, Ext JS, LLC.
26415  *
26416  * Originally Released Under LGPL - original licence link has changed is not relivant.
26417  *
26418  * Fork - LGPL
26419  * <script type="text/javascript">
26420  */
26421  
26422 /**
26423  * @class Roo.form.Layout
26424  * @extends Roo.Component
26425  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26426  * @constructor
26427  * @param {Object} config Configuration options
26428  */
26429 Roo.form.Layout = function(config){
26430     var xitems = [];
26431     if (config.items) {
26432         xitems = config.items;
26433         delete config.items;
26434     }
26435     Roo.form.Layout.superclass.constructor.call(this, config);
26436     this.stack = [];
26437     Roo.each(xitems, this.addxtype, this);
26438      
26439 };
26440
26441 Roo.extend(Roo.form.Layout, Roo.Component, {
26442     /**
26443      * @cfg {String/Object} autoCreate
26444      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26445      */
26446     /**
26447      * @cfg {String/Object/Function} style
26448      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26449      * a function which returns such a specification.
26450      */
26451     /**
26452      * @cfg {String} labelAlign
26453      * Valid values are "left," "top" and "right" (defaults to "left")
26454      */
26455     /**
26456      * @cfg {Number} labelWidth
26457      * Fixed width in pixels of all field labels (defaults to undefined)
26458      */
26459     /**
26460      * @cfg {Boolean} clear
26461      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26462      */
26463     clear : true,
26464     /**
26465      * @cfg {String} labelSeparator
26466      * The separator to use after field labels (defaults to ':')
26467      */
26468     labelSeparator : ':',
26469     /**
26470      * @cfg {Boolean} hideLabels
26471      * True to suppress the display of field labels in this layout (defaults to false)
26472      */
26473     hideLabels : false,
26474
26475     // private
26476     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26477     
26478     isLayout : true,
26479     
26480     // private
26481     onRender : function(ct, position){
26482         if(this.el){ // from markup
26483             this.el = Roo.get(this.el);
26484         }else {  // generate
26485             var cfg = this.getAutoCreate();
26486             this.el = ct.createChild(cfg, position);
26487         }
26488         if(this.style){
26489             this.el.applyStyles(this.style);
26490         }
26491         if(this.labelAlign){
26492             this.el.addClass('x-form-label-'+this.labelAlign);
26493         }
26494         if(this.hideLabels){
26495             this.labelStyle = "display:none";
26496             this.elementStyle = "padding-left:0;";
26497         }else{
26498             if(typeof this.labelWidth == 'number'){
26499                 this.labelStyle = "width:"+this.labelWidth+"px;";
26500                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26501             }
26502             if(this.labelAlign == 'top'){
26503                 this.labelStyle = "width:auto;";
26504                 this.elementStyle = "padding-left:0;";
26505             }
26506         }
26507         var stack = this.stack;
26508         var slen = stack.length;
26509         if(slen > 0){
26510             if(!this.fieldTpl){
26511                 var t = new Roo.Template(
26512                     '<div class="x-form-item {5}">',
26513                         '<label for="{0}" style="{2}">{1}{4}</label>',
26514                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26515                         '</div>',
26516                     '</div><div class="x-form-clear-left"></div>'
26517                 );
26518                 t.disableFormats = true;
26519                 t.compile();
26520                 Roo.form.Layout.prototype.fieldTpl = t;
26521             }
26522             for(var i = 0; i < slen; i++) {
26523                 if(stack[i].isFormField){
26524                     this.renderField(stack[i]);
26525                 }else{
26526                     this.renderComponent(stack[i]);
26527                 }
26528             }
26529         }
26530         if(this.clear){
26531             this.el.createChild({cls:'x-form-clear'});
26532         }
26533     },
26534
26535     // private
26536     renderField : function(f){
26537         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26538                f.id, //0
26539                f.fieldLabel, //1
26540                f.labelStyle||this.labelStyle||'', //2
26541                this.elementStyle||'', //3
26542                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26543                f.itemCls||this.itemCls||''  //5
26544        ], true).getPrevSibling());
26545     },
26546
26547     // private
26548     renderComponent : function(c){
26549         c.render(c.isLayout ? this.el : this.el.createChild());    
26550     },
26551     /**
26552      * Adds a object form elements (using the xtype property as the factory method.)
26553      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26554      * @param {Object} config 
26555      */
26556     addxtype : function(o)
26557     {
26558         // create the lement.
26559         o.form = this.form;
26560         var fe = Roo.factory(o, Roo.form);
26561         this.form.allItems.push(fe);
26562         this.stack.push(fe);
26563         
26564         if (fe.isFormField) {
26565             this.form.items.add(fe);
26566         }
26567          
26568         return fe;
26569     }
26570 });
26571
26572 /**
26573  * @class Roo.form.Column
26574  * @extends Roo.form.Layout
26575  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26576  * @constructor
26577  * @param {Object} config Configuration options
26578  */
26579 Roo.form.Column = function(config){
26580     Roo.form.Column.superclass.constructor.call(this, config);
26581 };
26582
26583 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26584     /**
26585      * @cfg {Number/String} width
26586      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26587      */
26588     /**
26589      * @cfg {String/Object} autoCreate
26590      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26591      */
26592
26593     // private
26594     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26595
26596     // private
26597     onRender : function(ct, position){
26598         Roo.form.Column.superclass.onRender.call(this, ct, position);
26599         if(this.width){
26600             this.el.setWidth(this.width);
26601         }
26602     }
26603 });
26604
26605
26606 /**
26607  * @class Roo.form.Row
26608  * @extends Roo.form.Layout
26609  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26610  * @constructor
26611  * @param {Object} config Configuration options
26612  */
26613
26614  
26615 Roo.form.Row = function(config){
26616     Roo.form.Row.superclass.constructor.call(this, config);
26617 };
26618  
26619 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26620       /**
26621      * @cfg {Number/String} width
26622      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26623      */
26624     /**
26625      * @cfg {Number/String} height
26626      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26627      */
26628     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26629     
26630     padWidth : 20,
26631     // private
26632     onRender : function(ct, position){
26633         //console.log('row render');
26634         if(!this.rowTpl){
26635             var t = new Roo.Template(
26636                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26637                     '<label for="{0}" style="{2}">{1}{4}</label>',
26638                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26639                     '</div>',
26640                 '</div>'
26641             );
26642             t.disableFormats = true;
26643             t.compile();
26644             Roo.form.Layout.prototype.rowTpl = t;
26645         }
26646         this.fieldTpl = this.rowTpl;
26647         
26648         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26649         var labelWidth = 100;
26650         
26651         if ((this.labelAlign != 'top')) {
26652             if (typeof this.labelWidth == 'number') {
26653                 labelWidth = this.labelWidth
26654             }
26655             this.padWidth =  20 + labelWidth;
26656             
26657         }
26658         
26659         Roo.form.Column.superclass.onRender.call(this, ct, position);
26660         if(this.width){
26661             this.el.setWidth(this.width);
26662         }
26663         if(this.height){
26664             this.el.setHeight(this.height);
26665         }
26666     },
26667     
26668     // private
26669     renderField : function(f){
26670         f.fieldEl = this.fieldTpl.append(this.el, [
26671                f.id, f.fieldLabel,
26672                f.labelStyle||this.labelStyle||'',
26673                this.elementStyle||'',
26674                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26675                f.itemCls||this.itemCls||'',
26676                f.width ? f.width + this.padWidth : 160 + this.padWidth
26677        ],true);
26678     }
26679 });
26680  
26681
26682 /**
26683  * @class Roo.form.FieldSet
26684  * @extends Roo.form.Layout
26685  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26686  * @constructor
26687  * @param {Object} config Configuration options
26688  */
26689 Roo.form.FieldSet = function(config){
26690     Roo.form.FieldSet.superclass.constructor.call(this, config);
26691 };
26692
26693 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26694     /**
26695      * @cfg {String} legend
26696      * The text to display as the legend for the FieldSet (defaults to '')
26697      */
26698     /**
26699      * @cfg {String/Object} autoCreate
26700      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26701      */
26702
26703     // private
26704     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26705
26706     // private
26707     onRender : function(ct, position){
26708         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26709         if(this.legend){
26710             this.setLegend(this.legend);
26711         }
26712     },
26713
26714     // private
26715     setLegend : function(text){
26716         if(this.rendered){
26717             this.el.child('legend').update(text);
26718         }
26719     }
26720 });/*
26721  * Based on:
26722  * Ext JS Library 1.1.1
26723  * Copyright(c) 2006-2007, Ext JS, LLC.
26724  *
26725  * Originally Released Under LGPL - original licence link has changed is not relivant.
26726  *
26727  * Fork - LGPL
26728  * <script type="text/javascript">
26729  */
26730 /**
26731  * @class Roo.form.VTypes
26732  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26733  * @singleton
26734  */
26735 Roo.form.VTypes = function(){
26736     // closure these in so they are only created once.
26737     var alpha = /^[a-zA-Z_]+$/;
26738     var alphanum = /^[a-zA-Z0-9_]+$/;
26739     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26740     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26741
26742     // All these messages and functions are configurable
26743     return {
26744         /**
26745          * The function used to validate email addresses
26746          * @param {String} value The email address
26747          */
26748         'email' : function(v){
26749             return email.test(v);
26750         },
26751         /**
26752          * The error text to display when the email validation function returns false
26753          * @type String
26754          */
26755         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26756         /**
26757          * The keystroke filter mask to be applied on email input
26758          * @type RegExp
26759          */
26760         'emailMask' : /[a-z0-9_\.\-@]/i,
26761
26762         /**
26763          * The function used to validate URLs
26764          * @param {String} value The URL
26765          */
26766         'url' : function(v){
26767             return url.test(v);
26768         },
26769         /**
26770          * The error text to display when the url validation function returns false
26771          * @type String
26772          */
26773         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26774         
26775         /**
26776          * The function used to validate alpha values
26777          * @param {String} value The value
26778          */
26779         'alpha' : function(v){
26780             return alpha.test(v);
26781         },
26782         /**
26783          * The error text to display when the alpha validation function returns false
26784          * @type String
26785          */
26786         'alphaText' : 'This field should only contain letters and _',
26787         /**
26788          * The keystroke filter mask to be applied on alpha input
26789          * @type RegExp
26790          */
26791         'alphaMask' : /[a-z_]/i,
26792
26793         /**
26794          * The function used to validate alphanumeric values
26795          * @param {String} value The value
26796          */
26797         'alphanum' : function(v){
26798             return alphanum.test(v);
26799         },
26800         /**
26801          * The error text to display when the alphanumeric validation function returns false
26802          * @type String
26803          */
26804         'alphanumText' : 'This field should only contain letters, numbers and _',
26805         /**
26806          * The keystroke filter mask to be applied on alphanumeric input
26807          * @type RegExp
26808          */
26809         'alphanumMask' : /[a-z0-9_]/i
26810     };
26811 }();//<script type="text/javascript">
26812
26813 /**
26814  * @class Roo.form.FCKeditor
26815  * @extends Roo.form.TextArea
26816  * Wrapper around the FCKEditor http://www.fckeditor.net
26817  * @constructor
26818  * Creates a new FCKeditor
26819  * @param {Object} config Configuration options
26820  */
26821 Roo.form.FCKeditor = function(config){
26822     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26823     this.addEvents({
26824          /**
26825          * @event editorinit
26826          * Fired when the editor is initialized - you can add extra handlers here..
26827          * @param {FCKeditor} this
26828          * @param {Object} the FCK object.
26829          */
26830         editorinit : true
26831     });
26832     
26833     
26834 };
26835 Roo.form.FCKeditor.editors = { };
26836 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26837 {
26838     //defaultAutoCreate : {
26839     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26840     //},
26841     // private
26842     /**
26843      * @cfg {Object} fck options - see fck manual for details.
26844      */
26845     fckconfig : false,
26846     
26847     /**
26848      * @cfg {Object} fck toolbar set (Basic or Default)
26849      */
26850     toolbarSet : 'Basic',
26851     /**
26852      * @cfg {Object} fck BasePath
26853      */ 
26854     basePath : '/fckeditor/',
26855     
26856     
26857     frame : false,
26858     
26859     value : '',
26860     
26861    
26862     onRender : function(ct, position)
26863     {
26864         if(!this.el){
26865             this.defaultAutoCreate = {
26866                 tag: "textarea",
26867                 style:"width:300px;height:60px;",
26868                 autocomplete: "new-password"
26869             };
26870         }
26871         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26872         /*
26873         if(this.grow){
26874             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26875             if(this.preventScrollbars){
26876                 this.el.setStyle("overflow", "hidden");
26877             }
26878             this.el.setHeight(this.growMin);
26879         }
26880         */
26881         //console.log('onrender' + this.getId() );
26882         Roo.form.FCKeditor.editors[this.getId()] = this;
26883          
26884
26885         this.replaceTextarea() ;
26886         
26887     },
26888     
26889     getEditor : function() {
26890         return this.fckEditor;
26891     },
26892     /**
26893      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26894      * @param {Mixed} value The value to set
26895      */
26896     
26897     
26898     setValue : function(value)
26899     {
26900         //console.log('setValue: ' + value);
26901         
26902         if(typeof(value) == 'undefined') { // not sure why this is happending...
26903             return;
26904         }
26905         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26906         
26907         //if(!this.el || !this.getEditor()) {
26908         //    this.value = value;
26909             //this.setValue.defer(100,this,[value]);    
26910         //    return;
26911         //} 
26912         
26913         if(!this.getEditor()) {
26914             return;
26915         }
26916         
26917         this.getEditor().SetData(value);
26918         
26919         //
26920
26921     },
26922
26923     /**
26924      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26925      * @return {Mixed} value The field value
26926      */
26927     getValue : function()
26928     {
26929         
26930         if (this.frame && this.frame.dom.style.display == 'none') {
26931             return Roo.form.FCKeditor.superclass.getValue.call(this);
26932         }
26933         
26934         if(!this.el || !this.getEditor()) {
26935            
26936            // this.getValue.defer(100,this); 
26937             return this.value;
26938         }
26939        
26940         
26941         var value=this.getEditor().GetData();
26942         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26943         return Roo.form.FCKeditor.superclass.getValue.call(this);
26944         
26945
26946     },
26947
26948     /**
26949      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26950      * @return {Mixed} value The field value
26951      */
26952     getRawValue : function()
26953     {
26954         if (this.frame && this.frame.dom.style.display == 'none') {
26955             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26956         }
26957         
26958         if(!this.el || !this.getEditor()) {
26959             //this.getRawValue.defer(100,this); 
26960             return this.value;
26961             return;
26962         }
26963         
26964         
26965         
26966         var value=this.getEditor().GetData();
26967         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26968         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26969          
26970     },
26971     
26972     setSize : function(w,h) {
26973         
26974         
26975         
26976         //if (this.frame && this.frame.dom.style.display == 'none') {
26977         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26978         //    return;
26979         //}
26980         //if(!this.el || !this.getEditor()) {
26981         //    this.setSize.defer(100,this, [w,h]); 
26982         //    return;
26983         //}
26984         
26985         
26986         
26987         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26988         
26989         this.frame.dom.setAttribute('width', w);
26990         this.frame.dom.setAttribute('height', h);
26991         this.frame.setSize(w,h);
26992         
26993     },
26994     
26995     toggleSourceEdit : function(value) {
26996         
26997       
26998          
26999         this.el.dom.style.display = value ? '' : 'none';
27000         this.frame.dom.style.display = value ?  'none' : '';
27001         
27002     },
27003     
27004     
27005     focus: function(tag)
27006     {
27007         if (this.frame.dom.style.display == 'none') {
27008             return Roo.form.FCKeditor.superclass.focus.call(this);
27009         }
27010         if(!this.el || !this.getEditor()) {
27011             this.focus.defer(100,this, [tag]); 
27012             return;
27013         }
27014         
27015         
27016         
27017         
27018         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
27019         this.getEditor().Focus();
27020         if (tgs.length) {
27021             if (!this.getEditor().Selection.GetSelection()) {
27022                 this.focus.defer(100,this, [tag]); 
27023                 return;
27024             }
27025             
27026             
27027             var r = this.getEditor().EditorDocument.createRange();
27028             r.setStart(tgs[0],0);
27029             r.setEnd(tgs[0],0);
27030             this.getEditor().Selection.GetSelection().removeAllRanges();
27031             this.getEditor().Selection.GetSelection().addRange(r);
27032             this.getEditor().Focus();
27033         }
27034         
27035     },
27036     
27037     
27038     
27039     replaceTextarea : function()
27040     {
27041         if ( document.getElementById( this.getId() + '___Frame' ) ) {
27042             return ;
27043         }
27044         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
27045         //{
27046             // We must check the elements firstly using the Id and then the name.
27047         var oTextarea = document.getElementById( this.getId() );
27048         
27049         var colElementsByName = document.getElementsByName( this.getId() ) ;
27050          
27051         oTextarea.style.display = 'none' ;
27052
27053         if ( oTextarea.tabIndex ) {            
27054             this.TabIndex = oTextarea.tabIndex ;
27055         }
27056         
27057         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
27058         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
27059         this.frame = Roo.get(this.getId() + '___Frame')
27060     },
27061     
27062     _getConfigHtml : function()
27063     {
27064         var sConfig = '' ;
27065
27066         for ( var o in this.fckconfig ) {
27067             sConfig += sConfig.length > 0  ? '&amp;' : '';
27068             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
27069         }
27070
27071         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
27072     },
27073     
27074     
27075     _getIFrameHtml : function()
27076     {
27077         var sFile = 'fckeditor.html' ;
27078         /* no idea what this is about..
27079         try
27080         {
27081             if ( (/fcksource=true/i).test( window.top.location.search ) )
27082                 sFile = 'fckeditor.original.html' ;
27083         }
27084         catch (e) { 
27085         */
27086
27087         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
27088         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
27089         
27090         
27091         var html = '<iframe id="' + this.getId() +
27092             '___Frame" src="' + sLink +
27093             '" width="' + this.width +
27094             '" height="' + this.height + '"' +
27095             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
27096             ' frameborder="0" scrolling="no"></iframe>' ;
27097
27098         return html ;
27099     },
27100     
27101     _insertHtmlBefore : function( html, element )
27102     {
27103         if ( element.insertAdjacentHTML )       {
27104             // IE
27105             element.insertAdjacentHTML( 'beforeBegin', html ) ;
27106         } else { // Gecko
27107             var oRange = document.createRange() ;
27108             oRange.setStartBefore( element ) ;
27109             var oFragment = oRange.createContextualFragment( html );
27110             element.parentNode.insertBefore( oFragment, element ) ;
27111         }
27112     }
27113     
27114     
27115   
27116     
27117     
27118     
27119     
27120
27121 });
27122
27123 //Roo.reg('fckeditor', Roo.form.FCKeditor);
27124
27125 function FCKeditor_OnComplete(editorInstance){
27126     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
27127     f.fckEditor = editorInstance;
27128     //console.log("loaded");
27129     f.fireEvent('editorinit', f, editorInstance);
27130
27131   
27132
27133  
27134
27135
27136
27137
27138
27139
27140
27141
27142
27143
27144
27145
27146
27147
27148
27149 //<script type="text/javascript">
27150 /**
27151  * @class Roo.form.GridField
27152  * @extends Roo.form.Field
27153  * Embed a grid (or editable grid into a form)
27154  * STATUS ALPHA
27155  * 
27156  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
27157  * it needs 
27158  * xgrid.store = Roo.data.Store
27159  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
27160  * xgrid.store.reader = Roo.data.JsonReader 
27161  * 
27162  * 
27163  * @constructor
27164  * Creates a new GridField
27165  * @param {Object} config Configuration options
27166  */
27167 Roo.form.GridField = function(config){
27168     Roo.form.GridField.superclass.constructor.call(this, config);
27169      
27170 };
27171
27172 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
27173     /**
27174      * @cfg {Number} width  - used to restrict width of grid..
27175      */
27176     width : 100,
27177     /**
27178      * @cfg {Number} height - used to restrict height of grid..
27179      */
27180     height : 50,
27181      /**
27182      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
27183          * 
27184          *}
27185      */
27186     xgrid : false, 
27187     /**
27188      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27189      * {tag: "input", type: "checkbox", autocomplete: "off"})
27190      */
27191    // defaultAutoCreate : { tag: 'div' },
27192     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
27193     /**
27194      * @cfg {String} addTitle Text to include for adding a title.
27195      */
27196     addTitle : false,
27197     //
27198     onResize : function(){
27199         Roo.form.Field.superclass.onResize.apply(this, arguments);
27200     },
27201
27202     initEvents : function(){
27203         // Roo.form.Checkbox.superclass.initEvents.call(this);
27204         // has no events...
27205        
27206     },
27207
27208
27209     getResizeEl : function(){
27210         return this.wrap;
27211     },
27212
27213     getPositionEl : function(){
27214         return this.wrap;
27215     },
27216
27217     // private
27218     onRender : function(ct, position){
27219         
27220         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
27221         var style = this.style;
27222         delete this.style;
27223         
27224         Roo.form.GridField.superclass.onRender.call(this, ct, position);
27225         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
27226         this.viewEl = this.wrap.createChild({ tag: 'div' });
27227         if (style) {
27228             this.viewEl.applyStyles(style);
27229         }
27230         if (this.width) {
27231             this.viewEl.setWidth(this.width);
27232         }
27233         if (this.height) {
27234             this.viewEl.setHeight(this.height);
27235         }
27236         //if(this.inputValue !== undefined){
27237         //this.setValue(this.value);
27238         
27239         
27240         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
27241         
27242         
27243         this.grid.render();
27244         this.grid.getDataSource().on('remove', this.refreshValue, this);
27245         this.grid.getDataSource().on('update', this.refreshValue, this);
27246         this.grid.on('afteredit', this.refreshValue, this);
27247  
27248     },
27249      
27250     
27251     /**
27252      * Sets the value of the item. 
27253      * @param {String} either an object  or a string..
27254      */
27255     setValue : function(v){
27256         //this.value = v;
27257         v = v || []; // empty set..
27258         // this does not seem smart - it really only affects memoryproxy grids..
27259         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
27260             var ds = this.grid.getDataSource();
27261             // assumes a json reader..
27262             var data = {}
27263             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
27264             ds.loadData( data);
27265         }
27266         // clear selection so it does not get stale.
27267         if (this.grid.sm) { 
27268             this.grid.sm.clearSelections();
27269         }
27270         
27271         Roo.form.GridField.superclass.setValue.call(this, v);
27272         this.refreshValue();
27273         // should load data in the grid really....
27274     },
27275     
27276     // private
27277     refreshValue: function() {
27278          var val = [];
27279         this.grid.getDataSource().each(function(r) {
27280             val.push(r.data);
27281         });
27282         this.el.dom.value = Roo.encode(val);
27283     }
27284     
27285      
27286     
27287     
27288 });/*
27289  * Based on:
27290  * Ext JS Library 1.1.1
27291  * Copyright(c) 2006-2007, Ext JS, LLC.
27292  *
27293  * Originally Released Under LGPL - original licence link has changed is not relivant.
27294  *
27295  * Fork - LGPL
27296  * <script type="text/javascript">
27297  */
27298 /**
27299  * @class Roo.form.DisplayField
27300  * @extends Roo.form.Field
27301  * A generic Field to display non-editable data.
27302  * @cfg {Boolean} closable (true|false) default false
27303  * @constructor
27304  * Creates a new Display Field item.
27305  * @param {Object} config Configuration options
27306  */
27307 Roo.form.DisplayField = function(config){
27308     Roo.form.DisplayField.superclass.constructor.call(this, config);
27309     
27310     this.addEvents({
27311         /**
27312          * @event close
27313          * Fires after the click the close btn
27314              * @param {Roo.form.DisplayField} this
27315              */
27316         close : true
27317     });
27318 };
27319
27320 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
27321     inputType:      'hidden',
27322     allowBlank:     true,
27323     readOnly:         true,
27324     
27325  
27326     /**
27327      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27328      */
27329     focusClass : undefined,
27330     /**
27331      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27332      */
27333     fieldClass: 'x-form-field',
27334     
27335      /**
27336      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27337      */
27338     valueRenderer: undefined,
27339     
27340     width: 100,
27341     /**
27342      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27343      * {tag: "input", type: "checkbox", autocomplete: "off"})
27344      */
27345      
27346  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27347  
27348     closable : false,
27349     
27350     onResize : function(){
27351         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27352         
27353     },
27354
27355     initEvents : function(){
27356         // Roo.form.Checkbox.superclass.initEvents.call(this);
27357         // has no events...
27358         
27359         if(this.closable){
27360             this.closeEl.on('click', this.onClose, this);
27361         }
27362        
27363     },
27364
27365
27366     getResizeEl : function(){
27367         return this.wrap;
27368     },
27369
27370     getPositionEl : function(){
27371         return this.wrap;
27372     },
27373
27374     // private
27375     onRender : function(ct, position){
27376         
27377         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27378         //if(this.inputValue !== undefined){
27379         this.wrap = this.el.wrap();
27380         
27381         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27382         
27383         if(this.closable){
27384             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27385         }
27386         
27387         if (this.bodyStyle) {
27388             this.viewEl.applyStyles(this.bodyStyle);
27389         }
27390         //this.viewEl.setStyle('padding', '2px');
27391         
27392         this.setValue(this.value);
27393         
27394     },
27395 /*
27396     // private
27397     initValue : Roo.emptyFn,
27398
27399   */
27400
27401         // private
27402     onClick : function(){
27403         
27404     },
27405
27406     /**
27407      * Sets the checked state of the checkbox.
27408      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27409      */
27410     setValue : function(v){
27411         this.value = v;
27412         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27413         // this might be called before we have a dom element..
27414         if (!this.viewEl) {
27415             return;
27416         }
27417         this.viewEl.dom.innerHTML = html;
27418         Roo.form.DisplayField.superclass.setValue.call(this, v);
27419
27420     },
27421     
27422     onClose : function(e)
27423     {
27424         e.preventDefault();
27425         
27426         this.fireEvent('close', this);
27427     }
27428 });/*
27429  * 
27430  * Licence- LGPL
27431  * 
27432  */
27433
27434 /**
27435  * @class Roo.form.DayPicker
27436  * @extends Roo.form.Field
27437  * A Day picker show [M] [T] [W] ....
27438  * @constructor
27439  * Creates a new Day Picker
27440  * @param {Object} config Configuration options
27441  */
27442 Roo.form.DayPicker= function(config){
27443     Roo.form.DayPicker.superclass.constructor.call(this, config);
27444      
27445 };
27446
27447 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27448     /**
27449      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27450      */
27451     focusClass : undefined,
27452     /**
27453      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27454      */
27455     fieldClass: "x-form-field",
27456    
27457     /**
27458      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27459      * {tag: "input", type: "checkbox", autocomplete: "off"})
27460      */
27461     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27462     
27463    
27464     actionMode : 'viewEl', 
27465     //
27466     // private
27467  
27468     inputType : 'hidden',
27469     
27470      
27471     inputElement: false, // real input element?
27472     basedOn: false, // ????
27473     
27474     isFormField: true, // not sure where this is needed!!!!
27475
27476     onResize : function(){
27477         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27478         if(!this.boxLabel){
27479             this.el.alignTo(this.wrap, 'c-c');
27480         }
27481     },
27482
27483     initEvents : function(){
27484         Roo.form.Checkbox.superclass.initEvents.call(this);
27485         this.el.on("click", this.onClick,  this);
27486         this.el.on("change", this.onClick,  this);
27487     },
27488
27489
27490     getResizeEl : function(){
27491         return this.wrap;
27492     },
27493
27494     getPositionEl : function(){
27495         return this.wrap;
27496     },
27497
27498     
27499     // private
27500     onRender : function(ct, position){
27501         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27502        
27503         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27504         
27505         var r1 = '<table><tr>';
27506         var r2 = '<tr class="x-form-daypick-icons">';
27507         for (var i=0; i < 7; i++) {
27508             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27509             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27510         }
27511         
27512         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27513         viewEl.select('img').on('click', this.onClick, this);
27514         this.viewEl = viewEl;   
27515         
27516         
27517         // this will not work on Chrome!!!
27518         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27519         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27520         
27521         
27522           
27523
27524     },
27525
27526     // private
27527     initValue : Roo.emptyFn,
27528
27529     /**
27530      * Returns the checked state of the checkbox.
27531      * @return {Boolean} True if checked, else false
27532      */
27533     getValue : function(){
27534         return this.el.dom.value;
27535         
27536     },
27537
27538         // private
27539     onClick : function(e){ 
27540         //this.setChecked(!this.checked);
27541         Roo.get(e.target).toggleClass('x-menu-item-checked');
27542         this.refreshValue();
27543         //if(this.el.dom.checked != this.checked){
27544         //    this.setValue(this.el.dom.checked);
27545        // }
27546     },
27547     
27548     // private
27549     refreshValue : function()
27550     {
27551         var val = '';
27552         this.viewEl.select('img',true).each(function(e,i,n)  {
27553             val += e.is(".x-menu-item-checked") ? String(n) : '';
27554         });
27555         this.setValue(val, true);
27556     },
27557
27558     /**
27559      * Sets the checked state of the checkbox.
27560      * On is always based on a string comparison between inputValue and the param.
27561      * @param {Boolean/String} value - the value to set 
27562      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27563      */
27564     setValue : function(v,suppressEvent){
27565         if (!this.el.dom) {
27566             return;
27567         }
27568         var old = this.el.dom.value ;
27569         this.el.dom.value = v;
27570         if (suppressEvent) {
27571             return ;
27572         }
27573          
27574         // update display..
27575         this.viewEl.select('img',true).each(function(e,i,n)  {
27576             
27577             var on = e.is(".x-menu-item-checked");
27578             var newv = v.indexOf(String(n)) > -1;
27579             if (on != newv) {
27580                 e.toggleClass('x-menu-item-checked');
27581             }
27582             
27583         });
27584         
27585         
27586         this.fireEvent('change', this, v, old);
27587         
27588         
27589     },
27590    
27591     // handle setting of hidden value by some other method!!?!?
27592     setFromHidden: function()
27593     {
27594         if(!this.el){
27595             return;
27596         }
27597         //console.log("SET FROM HIDDEN");
27598         //alert('setFrom hidden');
27599         this.setValue(this.el.dom.value);
27600     },
27601     
27602     onDestroy : function()
27603     {
27604         if(this.viewEl){
27605             Roo.get(this.viewEl).remove();
27606         }
27607          
27608         Roo.form.DayPicker.superclass.onDestroy.call(this);
27609     }
27610
27611 });/*
27612  * RooJS Library 1.1.1
27613  * Copyright(c) 2008-2011  Alan Knowles
27614  *
27615  * License - LGPL
27616  */
27617  
27618
27619 /**
27620  * @class Roo.form.ComboCheck
27621  * @extends Roo.form.ComboBox
27622  * A combobox for multiple select items.
27623  *
27624  * FIXME - could do with a reset button..
27625  * 
27626  * @constructor
27627  * Create a new ComboCheck
27628  * @param {Object} config Configuration options
27629  */
27630 Roo.form.ComboCheck = function(config){
27631     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27632     // should verify some data...
27633     // like
27634     // hiddenName = required..
27635     // displayField = required
27636     // valudField == required
27637     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27638     var _t = this;
27639     Roo.each(req, function(e) {
27640         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27641             throw "Roo.form.ComboCheck : missing value for: " + e;
27642         }
27643     });
27644     
27645     
27646 };
27647
27648 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27649      
27650      
27651     editable : false,
27652      
27653     selectedClass: 'x-menu-item-checked', 
27654     
27655     // private
27656     onRender : function(ct, position){
27657         var _t = this;
27658         
27659         
27660         
27661         if(!this.tpl){
27662             var cls = 'x-combo-list';
27663
27664             
27665             this.tpl =  new Roo.Template({
27666                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27667                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27668                    '<span>{' + this.displayField + '}</span>' +
27669                     '</div>' 
27670                 
27671             });
27672         }
27673  
27674         
27675         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27676         this.view.singleSelect = false;
27677         this.view.multiSelect = true;
27678         this.view.toggleSelect = true;
27679         this.pageTb.add(new Roo.Toolbar.Fill(), {
27680             
27681             text: 'Done',
27682             handler: function()
27683             {
27684                 _t.collapse();
27685             }
27686         });
27687     },
27688     
27689     onViewOver : function(e, t){
27690         // do nothing...
27691         return;
27692         
27693     },
27694     
27695     onViewClick : function(doFocus,index){
27696         return;
27697         
27698     },
27699     select: function () {
27700         //Roo.log("SELECT CALLED");
27701     },
27702      
27703     selectByValue : function(xv, scrollIntoView){
27704         var ar = this.getValueArray();
27705         var sels = [];
27706         
27707         Roo.each(ar, function(v) {
27708             if(v === undefined || v === null){
27709                 return;
27710             }
27711             var r = this.findRecord(this.valueField, v);
27712             if(r){
27713                 sels.push(this.store.indexOf(r))
27714                 
27715             }
27716         },this);
27717         this.view.select(sels);
27718         return false;
27719     },
27720     
27721     
27722     
27723     onSelect : function(record, index){
27724        // Roo.log("onselect Called");
27725        // this is only called by the clear button now..
27726         this.view.clearSelections();
27727         this.setValue('[]');
27728         if (this.value != this.valueBefore) {
27729             this.fireEvent('change', this, this.value, this.valueBefore);
27730             this.valueBefore = this.value;
27731         }
27732     },
27733     getValueArray : function()
27734     {
27735         var ar = [] ;
27736         
27737         try {
27738             //Roo.log(this.value);
27739             if (typeof(this.value) == 'undefined') {
27740                 return [];
27741             }
27742             var ar = Roo.decode(this.value);
27743             return  ar instanceof Array ? ar : []; //?? valid?
27744             
27745         } catch(e) {
27746             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27747             return [];
27748         }
27749          
27750     },
27751     expand : function ()
27752     {
27753         
27754         Roo.form.ComboCheck.superclass.expand.call(this);
27755         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27756         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27757         
27758
27759     },
27760     
27761     collapse : function(){
27762         Roo.form.ComboCheck.superclass.collapse.call(this);
27763         var sl = this.view.getSelectedIndexes();
27764         var st = this.store;
27765         var nv = [];
27766         var tv = [];
27767         var r;
27768         Roo.each(sl, function(i) {
27769             r = st.getAt(i);
27770             nv.push(r.get(this.valueField));
27771         },this);
27772         this.setValue(Roo.encode(nv));
27773         if (this.value != this.valueBefore) {
27774
27775             this.fireEvent('change', this, this.value, this.valueBefore);
27776             this.valueBefore = this.value;
27777         }
27778         
27779     },
27780     
27781     setValue : function(v){
27782         // Roo.log(v);
27783         this.value = v;
27784         
27785         var vals = this.getValueArray();
27786         var tv = [];
27787         Roo.each(vals, function(k) {
27788             var r = this.findRecord(this.valueField, k);
27789             if(r){
27790                 tv.push(r.data[this.displayField]);
27791             }else if(this.valueNotFoundText !== undefined){
27792                 tv.push( this.valueNotFoundText );
27793             }
27794         },this);
27795        // Roo.log(tv);
27796         
27797         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27798         this.hiddenField.value = v;
27799         this.value = v;
27800     }
27801     
27802 });/*
27803  * Based on:
27804  * Ext JS Library 1.1.1
27805  * Copyright(c) 2006-2007, Ext JS, LLC.
27806  *
27807  * Originally Released Under LGPL - original licence link has changed is not relivant.
27808  *
27809  * Fork - LGPL
27810  * <script type="text/javascript">
27811  */
27812  
27813 /**
27814  * @class Roo.form.Signature
27815  * @extends Roo.form.Field
27816  * Signature field.  
27817  * @constructor
27818  * 
27819  * @param {Object} config Configuration options
27820  */
27821
27822 Roo.form.Signature = function(config){
27823     Roo.form.Signature.superclass.constructor.call(this, config);
27824     
27825     this.addEvents({// not in used??
27826          /**
27827          * @event confirm
27828          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27829              * @param {Roo.form.Signature} combo This combo box
27830              */
27831         'confirm' : true,
27832         /**
27833          * @event reset
27834          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27835              * @param {Roo.form.ComboBox} combo This combo box
27836              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27837              */
27838         'reset' : true
27839     });
27840 };
27841
27842 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27843     /**
27844      * @cfg {Object} labels Label to use when rendering a form.
27845      * defaults to 
27846      * labels : { 
27847      *      clear : "Clear",
27848      *      confirm : "Confirm"
27849      *  }
27850      */
27851     labels : { 
27852         clear : "Clear",
27853         confirm : "Confirm"
27854     },
27855     /**
27856      * @cfg {Number} width The signature panel width (defaults to 300)
27857      */
27858     width: 300,
27859     /**
27860      * @cfg {Number} height The signature panel height (defaults to 100)
27861      */
27862     height : 100,
27863     /**
27864      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27865      */
27866     allowBlank : false,
27867     
27868     //private
27869     // {Object} signPanel The signature SVG panel element (defaults to {})
27870     signPanel : {},
27871     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27872     isMouseDown : false,
27873     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27874     isConfirmed : false,
27875     // {String} signatureTmp SVG mapping string (defaults to empty string)
27876     signatureTmp : '',
27877     
27878     
27879     defaultAutoCreate : { // modified by initCompnoent..
27880         tag: "input",
27881         type:"hidden"
27882     },
27883
27884     // private
27885     onRender : function(ct, position){
27886         
27887         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27888         
27889         this.wrap = this.el.wrap({
27890             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27891         });
27892         
27893         this.createToolbar(this);
27894         this.signPanel = this.wrap.createChild({
27895                 tag: 'div',
27896                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27897             }, this.el
27898         );
27899             
27900         this.svgID = Roo.id();
27901         this.svgEl = this.signPanel.createChild({
27902               xmlns : 'http://www.w3.org/2000/svg',
27903               tag : 'svg',
27904               id : this.svgID + "-svg",
27905               width: this.width,
27906               height: this.height,
27907               viewBox: '0 0 '+this.width+' '+this.height,
27908               cn : [
27909                 {
27910                     tag: "rect",
27911                     id: this.svgID + "-svg-r",
27912                     width: this.width,
27913                     height: this.height,
27914                     fill: "#ffa"
27915                 },
27916                 {
27917                     tag: "line",
27918                     id: this.svgID + "-svg-l",
27919                     x1: "0", // start
27920                     y1: (this.height*0.8), // start set the line in 80% of height
27921                     x2: this.width, // end
27922                     y2: (this.height*0.8), // end set the line in 80% of height
27923                     'stroke': "#666",
27924                     'stroke-width': "1",
27925                     'stroke-dasharray': "3",
27926                     'shape-rendering': "crispEdges",
27927                     'pointer-events': "none"
27928                 },
27929                 {
27930                     tag: "path",
27931                     id: this.svgID + "-svg-p",
27932                     'stroke': "navy",
27933                     'stroke-width': "3",
27934                     'fill': "none",
27935                     'pointer-events': 'none'
27936                 }
27937               ]
27938         });
27939         this.createSVG();
27940         this.svgBox = this.svgEl.dom.getScreenCTM();
27941     },
27942     createSVG : function(){ 
27943         var svg = this.signPanel;
27944         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27945         var t = this;
27946
27947         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27948         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27949         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27950         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27951         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27952         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27953         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27954         
27955     },
27956     isTouchEvent : function(e){
27957         return e.type.match(/^touch/);
27958     },
27959     getCoords : function (e) {
27960         var pt    = this.svgEl.dom.createSVGPoint();
27961         pt.x = e.clientX; 
27962         pt.y = e.clientY;
27963         if (this.isTouchEvent(e)) {
27964             pt.x =  e.targetTouches[0].clientX;
27965             pt.y = e.targetTouches[0].clientY;
27966         }
27967         var a = this.svgEl.dom.getScreenCTM();
27968         var b = a.inverse();
27969         var mx = pt.matrixTransform(b);
27970         return mx.x + ',' + mx.y;
27971     },
27972     //mouse event headler 
27973     down : function (e) {
27974         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27975         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27976         
27977         this.isMouseDown = true;
27978         
27979         e.preventDefault();
27980     },
27981     move : function (e) {
27982         if (this.isMouseDown) {
27983             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27984             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27985         }
27986         
27987         e.preventDefault();
27988     },
27989     up : function (e) {
27990         this.isMouseDown = false;
27991         var sp = this.signatureTmp.split(' ');
27992         
27993         if(sp.length > 1){
27994             if(!sp[sp.length-2].match(/^L/)){
27995                 sp.pop();
27996                 sp.pop();
27997                 sp.push("");
27998                 this.signatureTmp = sp.join(" ");
27999             }
28000         }
28001         if(this.getValue() != this.signatureTmp){
28002             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28003             this.isConfirmed = false;
28004         }
28005         e.preventDefault();
28006     },
28007     
28008     /**
28009      * Protected method that will not generally be called directly. It
28010      * is called when the editor creates its toolbar. Override this method if you need to
28011      * add custom toolbar buttons.
28012      * @param {HtmlEditor} editor
28013      */
28014     createToolbar : function(editor){
28015          function btn(id, toggle, handler){
28016             var xid = fid + '-'+ id ;
28017             return {
28018                 id : xid,
28019                 cmd : id,
28020                 cls : 'x-btn-icon x-edit-'+id,
28021                 enableToggle:toggle !== false,
28022                 scope: editor, // was editor...
28023                 handler:handler||editor.relayBtnCmd,
28024                 clickEvent:'mousedown',
28025                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
28026                 tabIndex:-1
28027             };
28028         }
28029         
28030         
28031         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
28032         this.tb = tb;
28033         this.tb.add(
28034            {
28035                 cls : ' x-signature-btn x-signature-'+id,
28036                 scope: editor, // was editor...
28037                 handler: this.reset,
28038                 clickEvent:'mousedown',
28039                 text: this.labels.clear
28040             },
28041             {
28042                  xtype : 'Fill',
28043                  xns: Roo.Toolbar
28044             }, 
28045             {
28046                 cls : '  x-signature-btn x-signature-'+id,
28047                 scope: editor, // was editor...
28048                 handler: this.confirmHandler,
28049                 clickEvent:'mousedown',
28050                 text: this.labels.confirm
28051             }
28052         );
28053     
28054     },
28055     //public
28056     /**
28057      * when user is clicked confirm then show this image.....
28058      * 
28059      * @return {String} Image Data URI
28060      */
28061     getImageDataURI : function(){
28062         var svg = this.svgEl.dom.parentNode.innerHTML;
28063         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
28064         return src; 
28065     },
28066     /**
28067      * 
28068      * @return {Boolean} this.isConfirmed
28069      */
28070     getConfirmed : function(){
28071         return this.isConfirmed;
28072     },
28073     /**
28074      * 
28075      * @return {Number} this.width
28076      */
28077     getWidth : function(){
28078         return this.width;
28079     },
28080     /**
28081      * 
28082      * @return {Number} this.height
28083      */
28084     getHeight : function(){
28085         return this.height;
28086     },
28087     // private
28088     getSignature : function(){
28089         return this.signatureTmp;
28090     },
28091     // private
28092     reset : function(){
28093         this.signatureTmp = '';
28094         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28095         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
28096         this.isConfirmed = false;
28097         Roo.form.Signature.superclass.reset.call(this);
28098     },
28099     setSignature : function(s){
28100         this.signatureTmp = s;
28101         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
28102         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
28103         this.setValue(s);
28104         this.isConfirmed = false;
28105         Roo.form.Signature.superclass.reset.call(this);
28106     }, 
28107     test : function(){
28108 //        Roo.log(this.signPanel.dom.contentWindow.up())
28109     },
28110     //private
28111     setConfirmed : function(){
28112         
28113         
28114         
28115 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
28116     },
28117     // private
28118     confirmHandler : function(){
28119         if(!this.getSignature()){
28120             return;
28121         }
28122         
28123         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
28124         this.setValue(this.getSignature());
28125         this.isConfirmed = true;
28126         
28127         this.fireEvent('confirm', this);
28128     },
28129     // private
28130     // Subclasses should provide the validation implementation by overriding this
28131     validateValue : function(value){
28132         if(this.allowBlank){
28133             return true;
28134         }
28135         
28136         if(this.isConfirmed){
28137             return true;
28138         }
28139         return false;
28140     }
28141 });/*
28142  * Based on:
28143  * Ext JS Library 1.1.1
28144  * Copyright(c) 2006-2007, Ext JS, LLC.
28145  *
28146  * Originally Released Under LGPL - original licence link has changed is not relivant.
28147  *
28148  * Fork - LGPL
28149  * <script type="text/javascript">
28150  */
28151  
28152
28153 /**
28154  * @class Roo.form.ComboBox
28155  * @extends Roo.form.TriggerField
28156  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
28157  * @constructor
28158  * Create a new ComboBox.
28159  * @param {Object} config Configuration options
28160  */
28161 Roo.form.Select = function(config){
28162     Roo.form.Select.superclass.constructor.call(this, config);
28163      
28164 };
28165
28166 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
28167     /**
28168      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
28169      */
28170     /**
28171      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
28172      * rendering into an Roo.Editor, defaults to false)
28173      */
28174     /**
28175      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
28176      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
28177      */
28178     /**
28179      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
28180      */
28181     /**
28182      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
28183      * the dropdown list (defaults to undefined, with no header element)
28184      */
28185
28186      /**
28187      * @cfg {String/Roo.Template} tpl The template to use to render the output
28188      */
28189      
28190     // private
28191     defaultAutoCreate : {tag: "select"  },
28192     /**
28193      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
28194      */
28195     listWidth: undefined,
28196     /**
28197      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
28198      * mode = 'remote' or 'text' if mode = 'local')
28199      */
28200     displayField: undefined,
28201     /**
28202      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
28203      * mode = 'remote' or 'value' if mode = 'local'). 
28204      * Note: use of a valueField requires the user make a selection
28205      * in order for a value to be mapped.
28206      */
28207     valueField: undefined,
28208     
28209     
28210     /**
28211      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
28212      * field's data value (defaults to the underlying DOM element's name)
28213      */
28214     hiddenName: undefined,
28215     /**
28216      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
28217      */
28218     listClass: '',
28219     /**
28220      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
28221      */
28222     selectedClass: 'x-combo-selected',
28223     /**
28224      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
28225      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
28226      * which displays a downward arrow icon).
28227      */
28228     triggerClass : 'x-form-arrow-trigger',
28229     /**
28230      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28231      */
28232     shadow:'sides',
28233     /**
28234      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
28235      * anchor positions (defaults to 'tl-bl')
28236      */
28237     listAlign: 'tl-bl?',
28238     /**
28239      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
28240      */
28241     maxHeight: 300,
28242     /**
28243      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
28244      * query specified by the allQuery config option (defaults to 'query')
28245      */
28246     triggerAction: 'query',
28247     /**
28248      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
28249      * (defaults to 4, does not apply if editable = false)
28250      */
28251     minChars : 4,
28252     /**
28253      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
28254      * delay (typeAheadDelay) if it matches a known value (defaults to false)
28255      */
28256     typeAhead: false,
28257     /**
28258      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
28259      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
28260      */
28261     queryDelay: 500,
28262     /**
28263      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
28264      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
28265      */
28266     pageSize: 0,
28267     /**
28268      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
28269      * when editable = true (defaults to false)
28270      */
28271     selectOnFocus:false,
28272     /**
28273      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
28274      */
28275     queryParam: 'query',
28276     /**
28277      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
28278      * when mode = 'remote' (defaults to 'Loading...')
28279      */
28280     loadingText: 'Loading...',
28281     /**
28282      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
28283      */
28284     resizable: false,
28285     /**
28286      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
28287      */
28288     handleHeight : 8,
28289     /**
28290      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
28291      * traditional select (defaults to true)
28292      */
28293     editable: true,
28294     /**
28295      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
28296      */
28297     allQuery: '',
28298     /**
28299      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
28300      */
28301     mode: 'remote',
28302     /**
28303      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
28304      * listWidth has a higher value)
28305      */
28306     minListWidth : 70,
28307     /**
28308      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
28309      * allow the user to set arbitrary text into the field (defaults to false)
28310      */
28311     forceSelection:false,
28312     /**
28313      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
28314      * if typeAhead = true (defaults to 250)
28315      */
28316     typeAheadDelay : 250,
28317     /**
28318      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
28319      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
28320      */
28321     valueNotFoundText : undefined,
28322     
28323     /**
28324      * @cfg {String} defaultValue The value displayed after loading the store.
28325      */
28326     defaultValue: '',
28327     
28328     /**
28329      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
28330      */
28331     blockFocus : false,
28332     
28333     /**
28334      * @cfg {Boolean} disableClear Disable showing of clear button.
28335      */
28336     disableClear : false,
28337     /**
28338      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28339      */
28340     alwaysQuery : false,
28341     
28342     //private
28343     addicon : false,
28344     editicon: false,
28345     
28346     // element that contains real text value.. (when hidden is used..)
28347      
28348     // private
28349     onRender : function(ct, position){
28350         Roo.form.Field.prototype.onRender.call(this, ct, position);
28351         
28352         if(this.store){
28353             this.store.on('beforeload', this.onBeforeLoad, this);
28354             this.store.on('load', this.onLoad, this);
28355             this.store.on('loadexception', this.onLoadException, this);
28356             this.store.load({});
28357         }
28358         
28359         
28360         
28361     },
28362
28363     // private
28364     initEvents : function(){
28365         //Roo.form.ComboBox.superclass.initEvents.call(this);
28366  
28367     },
28368
28369     onDestroy : function(){
28370        
28371         if(this.store){
28372             this.store.un('beforeload', this.onBeforeLoad, this);
28373             this.store.un('load', this.onLoad, this);
28374             this.store.un('loadexception', this.onLoadException, this);
28375         }
28376         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28377     },
28378
28379     // private
28380     fireKey : function(e){
28381         if(e.isNavKeyPress() && !this.list.isVisible()){
28382             this.fireEvent("specialkey", this, e);
28383         }
28384     },
28385
28386     // private
28387     onResize: function(w, h){
28388         
28389         return; 
28390     
28391         
28392     },
28393
28394     /**
28395      * Allow or prevent the user from directly editing the field text.  If false is passed,
28396      * the user will only be able to select from the items defined in the dropdown list.  This method
28397      * is the runtime equivalent of setting the 'editable' config option at config time.
28398      * @param {Boolean} value True to allow the user to directly edit the field text
28399      */
28400     setEditable : function(value){
28401          
28402     },
28403
28404     // private
28405     onBeforeLoad : function(){
28406         
28407         Roo.log("Select before load");
28408         return;
28409     
28410         this.innerList.update(this.loadingText ?
28411                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28412         //this.restrictHeight();
28413         this.selectedIndex = -1;
28414     },
28415
28416     // private
28417     onLoad : function(){
28418
28419     
28420         var dom = this.el.dom;
28421         dom.innerHTML = '';
28422          var od = dom.ownerDocument;
28423          
28424         if (this.emptyText) {
28425             var op = od.createElement('option');
28426             op.setAttribute('value', '');
28427             op.innerHTML = String.format('{0}', this.emptyText);
28428             dom.appendChild(op);
28429         }
28430         if(this.store.getCount() > 0){
28431            
28432             var vf = this.valueField;
28433             var df = this.displayField;
28434             this.store.data.each(function(r) {
28435                 // which colmsn to use... testing - cdoe / title..
28436                 var op = od.createElement('option');
28437                 op.setAttribute('value', r.data[vf]);
28438                 op.innerHTML = String.format('{0}', r.data[df]);
28439                 dom.appendChild(op);
28440             });
28441             if (typeof(this.defaultValue != 'undefined')) {
28442                 this.setValue(this.defaultValue);
28443             }
28444             
28445              
28446         }else{
28447             //this.onEmptyResults();
28448         }
28449         //this.el.focus();
28450     },
28451     // private
28452     onLoadException : function()
28453     {
28454         dom.innerHTML = '';
28455             
28456         Roo.log("Select on load exception");
28457         return;
28458     
28459         this.collapse();
28460         Roo.log(this.store.reader.jsonData);
28461         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28462             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28463         }
28464         
28465         
28466     },
28467     // private
28468     onTypeAhead : function(){
28469          
28470     },
28471
28472     // private
28473     onSelect : function(record, index){
28474         Roo.log('on select?');
28475         return;
28476         if(this.fireEvent('beforeselect', this, record, index) !== false){
28477             this.setFromData(index > -1 ? record.data : false);
28478             this.collapse();
28479             this.fireEvent('select', this, record, index);
28480         }
28481     },
28482
28483     /**
28484      * Returns the currently selected field value or empty string if no value is set.
28485      * @return {String} value The selected value
28486      */
28487     getValue : function(){
28488         var dom = this.el.dom;
28489         this.value = dom.options[dom.selectedIndex].value;
28490         return this.value;
28491         
28492     },
28493
28494     /**
28495      * Clears any text/value currently set in the field
28496      */
28497     clearValue : function(){
28498         this.value = '';
28499         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28500         
28501     },
28502
28503     /**
28504      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28505      * will be displayed in the field.  If the value does not match the data value of an existing item,
28506      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28507      * Otherwise the field will be blank (although the value will still be set).
28508      * @param {String} value The value to match
28509      */
28510     setValue : function(v){
28511         var d = this.el.dom;
28512         for (var i =0; i < d.options.length;i++) {
28513             if (v == d.options[i].value) {
28514                 d.selectedIndex = i;
28515                 this.value = v;
28516                 return;
28517             }
28518         }
28519         this.clearValue();
28520     },
28521     /**
28522      * @property {Object} the last set data for the element
28523      */
28524     
28525     lastData : false,
28526     /**
28527      * Sets the value of the field based on a object which is related to the record format for the store.
28528      * @param {Object} value the value to set as. or false on reset?
28529      */
28530     setFromData : function(o){
28531         Roo.log('setfrom data?');
28532          
28533         
28534         
28535     },
28536     // private
28537     reset : function(){
28538         this.clearValue();
28539     },
28540     // private
28541     findRecord : function(prop, value){
28542         
28543         return false;
28544     
28545         var record;
28546         if(this.store.getCount() > 0){
28547             this.store.each(function(r){
28548                 if(r.data[prop] == value){
28549                     record = r;
28550                     return false;
28551                 }
28552                 return true;
28553             });
28554         }
28555         return record;
28556     },
28557     
28558     getName: function()
28559     {
28560         // returns hidden if it's set..
28561         if (!this.rendered) {return ''};
28562         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28563         
28564     },
28565      
28566
28567     
28568
28569     // private
28570     onEmptyResults : function(){
28571         Roo.log('empty results');
28572         //this.collapse();
28573     },
28574
28575     /**
28576      * Returns true if the dropdown list is expanded, else false.
28577      */
28578     isExpanded : function(){
28579         return false;
28580     },
28581
28582     /**
28583      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28584      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28585      * @param {String} value The data value of the item to select
28586      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28587      * selected item if it is not currently in view (defaults to true)
28588      * @return {Boolean} True if the value matched an item in the list, else false
28589      */
28590     selectByValue : function(v, scrollIntoView){
28591         Roo.log('select By Value');
28592         return false;
28593     
28594         if(v !== undefined && v !== null){
28595             var r = this.findRecord(this.valueField || this.displayField, v);
28596             if(r){
28597                 this.select(this.store.indexOf(r), scrollIntoView);
28598                 return true;
28599             }
28600         }
28601         return false;
28602     },
28603
28604     /**
28605      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28606      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28607      * @param {Number} index The zero-based index of the list item to select
28608      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28609      * selected item if it is not currently in view (defaults to true)
28610      */
28611     select : function(index, scrollIntoView){
28612         Roo.log('select ');
28613         return  ;
28614         
28615         this.selectedIndex = index;
28616         this.view.select(index);
28617         if(scrollIntoView !== false){
28618             var el = this.view.getNode(index);
28619             if(el){
28620                 this.innerList.scrollChildIntoView(el, false);
28621             }
28622         }
28623     },
28624
28625       
28626
28627     // private
28628     validateBlur : function(){
28629         
28630         return;
28631         
28632     },
28633
28634     // private
28635     initQuery : function(){
28636         this.doQuery(this.getRawValue());
28637     },
28638
28639     // private
28640     doForce : function(){
28641         if(this.el.dom.value.length > 0){
28642             this.el.dom.value =
28643                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28644              
28645         }
28646     },
28647
28648     /**
28649      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28650      * query allowing the query action to be canceled if needed.
28651      * @param {String} query The SQL query to execute
28652      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28653      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28654      * saved in the current store (defaults to false)
28655      */
28656     doQuery : function(q, forceAll){
28657         
28658         Roo.log('doQuery?');
28659         if(q === undefined || q === null){
28660             q = '';
28661         }
28662         var qe = {
28663             query: q,
28664             forceAll: forceAll,
28665             combo: this,
28666             cancel:false
28667         };
28668         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28669             return false;
28670         }
28671         q = qe.query;
28672         forceAll = qe.forceAll;
28673         if(forceAll === true || (q.length >= this.minChars)){
28674             if(this.lastQuery != q || this.alwaysQuery){
28675                 this.lastQuery = q;
28676                 if(this.mode == 'local'){
28677                     this.selectedIndex = -1;
28678                     if(forceAll){
28679                         this.store.clearFilter();
28680                     }else{
28681                         this.store.filter(this.displayField, q);
28682                     }
28683                     this.onLoad();
28684                 }else{
28685                     this.store.baseParams[this.queryParam] = q;
28686                     this.store.load({
28687                         params: this.getParams(q)
28688                     });
28689                     this.expand();
28690                 }
28691             }else{
28692                 this.selectedIndex = -1;
28693                 this.onLoad();   
28694             }
28695         }
28696     },
28697
28698     // private
28699     getParams : function(q){
28700         var p = {};
28701         //p[this.queryParam] = q;
28702         if(this.pageSize){
28703             p.start = 0;
28704             p.limit = this.pageSize;
28705         }
28706         return p;
28707     },
28708
28709     /**
28710      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28711      */
28712     collapse : function(){
28713         
28714     },
28715
28716     // private
28717     collapseIf : function(e){
28718         
28719     },
28720
28721     /**
28722      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28723      */
28724     expand : function(){
28725         
28726     } ,
28727
28728     // private
28729      
28730
28731     /** 
28732     * @cfg {Boolean} grow 
28733     * @hide 
28734     */
28735     /** 
28736     * @cfg {Number} growMin 
28737     * @hide 
28738     */
28739     /** 
28740     * @cfg {Number} growMax 
28741     * @hide 
28742     */
28743     /**
28744      * @hide
28745      * @method autoSize
28746      */
28747     
28748     setWidth : function()
28749     {
28750         
28751     },
28752     getResizeEl : function(){
28753         return this.el;
28754     }
28755 });//<script type="text/javasscript">
28756  
28757
28758 /**
28759  * @class Roo.DDView
28760  * A DnD enabled version of Roo.View.
28761  * @param {Element/String} container The Element in which to create the View.
28762  * @param {String} tpl The template string used to create the markup for each element of the View
28763  * @param {Object} config The configuration properties. These include all the config options of
28764  * {@link Roo.View} plus some specific to this class.<br>
28765  * <p>
28766  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28767  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28768  * <p>
28769  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28770 .x-view-drag-insert-above {
28771         border-top:1px dotted #3366cc;
28772 }
28773 .x-view-drag-insert-below {
28774         border-bottom:1px dotted #3366cc;
28775 }
28776 </code></pre>
28777  * 
28778  */
28779  
28780 Roo.DDView = function(container, tpl, config) {
28781     Roo.DDView.superclass.constructor.apply(this, arguments);
28782     this.getEl().setStyle("outline", "0px none");
28783     this.getEl().unselectable();
28784     if (this.dragGroup) {
28785                 this.setDraggable(this.dragGroup.split(","));
28786     }
28787     if (this.dropGroup) {
28788                 this.setDroppable(this.dropGroup.split(","));
28789     }
28790     if (this.deletable) {
28791         this.setDeletable();
28792     }
28793     this.isDirtyFlag = false;
28794         this.addEvents({
28795                 "drop" : true
28796         });
28797 };
28798
28799 Roo.extend(Roo.DDView, Roo.View, {
28800 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28801 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28802 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28803 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28804
28805         isFormField: true,
28806
28807         reset: Roo.emptyFn,
28808         
28809         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28810
28811         validate: function() {
28812                 return true;
28813         },
28814         
28815         destroy: function() {
28816                 this.purgeListeners();
28817                 this.getEl.removeAllListeners();
28818                 this.getEl().remove();
28819                 if (this.dragZone) {
28820                         if (this.dragZone.destroy) {
28821                                 this.dragZone.destroy();
28822                         }
28823                 }
28824                 if (this.dropZone) {
28825                         if (this.dropZone.destroy) {
28826                                 this.dropZone.destroy();
28827                         }
28828                 }
28829         },
28830
28831 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28832         getName: function() {
28833                 return this.name;
28834         },
28835
28836 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28837         setValue: function(v) {
28838                 if (!this.store) {
28839                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28840                 }
28841                 var data = {};
28842                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28843                 this.store.proxy = new Roo.data.MemoryProxy(data);
28844                 this.store.load();
28845         },
28846
28847 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28848         getValue: function() {
28849                 var result = '(';
28850                 this.store.each(function(rec) {
28851                         result += rec.id + ',';
28852                 });
28853                 return result.substr(0, result.length - 1) + ')';
28854         },
28855         
28856         getIds: function() {
28857                 var i = 0, result = new Array(this.store.getCount());
28858                 this.store.each(function(rec) {
28859                         result[i++] = rec.id;
28860                 });
28861                 return result;
28862         },
28863         
28864         isDirty: function() {
28865                 return this.isDirtyFlag;
28866         },
28867
28868 /**
28869  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28870  *      whole Element becomes the target, and this causes the drop gesture to append.
28871  */
28872     getTargetFromEvent : function(e) {
28873                 var target = e.getTarget();
28874                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28875                 target = target.parentNode;
28876                 }
28877                 if (!target) {
28878                         target = this.el.dom.lastChild || this.el.dom;
28879                 }
28880                 return target;
28881     },
28882
28883 /**
28884  *      Create the drag data which consists of an object which has the property "ddel" as
28885  *      the drag proxy element. 
28886  */
28887     getDragData : function(e) {
28888         var target = this.findItemFromChild(e.getTarget());
28889                 if(target) {
28890                         this.handleSelection(e);
28891                         var selNodes = this.getSelectedNodes();
28892             var dragData = {
28893                 source: this,
28894                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28895                 nodes: selNodes,
28896                 records: []
28897                         };
28898                         var selectedIndices = this.getSelectedIndexes();
28899                         for (var i = 0; i < selectedIndices.length; i++) {
28900                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28901                         }
28902                         if (selNodes.length == 1) {
28903                                 dragData.ddel = target.cloneNode(true); // the div element
28904                         } else {
28905                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28906                                 div.className = 'multi-proxy';
28907                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28908                                         div.appendChild(selNodes[i].cloneNode(true));
28909                                 }
28910                                 dragData.ddel = div;
28911                         }
28912             //console.log(dragData)
28913             //console.log(dragData.ddel.innerHTML)
28914                         return dragData;
28915                 }
28916         //console.log('nodragData')
28917                 return false;
28918     },
28919     
28920 /**     Specify to which ddGroup items in this DDView may be dragged. */
28921     setDraggable: function(ddGroup) {
28922         if (ddGroup instanceof Array) {
28923                 Roo.each(ddGroup, this.setDraggable, this);
28924                 return;
28925         }
28926         if (this.dragZone) {
28927                 this.dragZone.addToGroup(ddGroup);
28928         } else {
28929                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28930                                 containerScroll: true,
28931                                 ddGroup: ddGroup 
28932
28933                         });
28934 //                      Draggability implies selection. DragZone's mousedown selects the element.
28935                         if (!this.multiSelect) { this.singleSelect = true; }
28936
28937 //                      Wire the DragZone's handlers up to methods in *this*
28938                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28939                 }
28940     },
28941
28942 /**     Specify from which ddGroup this DDView accepts drops. */
28943     setDroppable: function(ddGroup) {
28944         if (ddGroup instanceof Array) {
28945                 Roo.each(ddGroup, this.setDroppable, this);
28946                 return;
28947         }
28948         if (this.dropZone) {
28949                 this.dropZone.addToGroup(ddGroup);
28950         } else {
28951                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28952                                 containerScroll: true,
28953                                 ddGroup: ddGroup
28954                         });
28955
28956 //                      Wire the DropZone's handlers up to methods in *this*
28957                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28958                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28959                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28960                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28961                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28962                 }
28963     },
28964
28965 /**     Decide whether to drop above or below a View node. */
28966     getDropPoint : function(e, n, dd){
28967         if (n == this.el.dom) { return "above"; }
28968                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28969                 var c = t + (b - t) / 2;
28970                 var y = Roo.lib.Event.getPageY(e);
28971                 if(y <= c) {
28972                         return "above";
28973                 }else{
28974                         return "below";
28975                 }
28976     },
28977
28978     onNodeEnter : function(n, dd, e, data){
28979                 return false;
28980     },
28981     
28982     onNodeOver : function(n, dd, e, data){
28983                 var pt = this.getDropPoint(e, n, dd);
28984                 // set the insert point style on the target node
28985                 var dragElClass = this.dropNotAllowed;
28986                 if (pt) {
28987                         var targetElClass;
28988                         if (pt == "above"){
28989                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28990                                 targetElClass = "x-view-drag-insert-above";
28991                         } else {
28992                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28993                                 targetElClass = "x-view-drag-insert-below";
28994                         }
28995                         if (this.lastInsertClass != targetElClass){
28996                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28997                                 this.lastInsertClass = targetElClass;
28998                         }
28999                 }
29000                 return dragElClass;
29001         },
29002
29003     onNodeOut : function(n, dd, e, data){
29004                 this.removeDropIndicators(n);
29005     },
29006
29007     onNodeDrop : function(n, dd, e, data){
29008         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
29009                 return false;
29010         }
29011         var pt = this.getDropPoint(e, n, dd);
29012                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
29013                 if (pt == "below") { insertAt++; }
29014                 for (var i = 0; i < data.records.length; i++) {
29015                         var r = data.records[i];
29016                         var dup = this.store.getById(r.id);
29017                         if (dup && (dd != this.dragZone)) {
29018                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
29019                         } else {
29020                                 if (data.copy) {
29021                                         this.store.insert(insertAt++, r.copy());
29022                                 } else {
29023                                         data.source.isDirtyFlag = true;
29024                                         r.store.remove(r);
29025                                         this.store.insert(insertAt++, r);
29026                                 }
29027                                 this.isDirtyFlag = true;
29028                         }
29029                 }
29030                 this.dragZone.cachedTarget = null;
29031                 return true;
29032     },
29033
29034     removeDropIndicators : function(n){
29035                 if(n){
29036                         Roo.fly(n).removeClass([
29037                                 "x-view-drag-insert-above",
29038                                 "x-view-drag-insert-below"]);
29039                         this.lastInsertClass = "_noclass";
29040                 }
29041     },
29042
29043 /**
29044  *      Utility method. Add a delete option to the DDView's context menu.
29045  *      @param {String} imageUrl The URL of the "delete" icon image.
29046  */
29047         setDeletable: function(imageUrl) {
29048                 if (!this.singleSelect && !this.multiSelect) {
29049                         this.singleSelect = true;
29050                 }
29051                 var c = this.getContextMenu();
29052                 this.contextMenu.on("itemclick", function(item) {
29053                         switch (item.id) {
29054                                 case "delete":
29055                                         this.remove(this.getSelectedIndexes());
29056                                         break;
29057                         }
29058                 }, this);
29059                 this.contextMenu.add({
29060                         icon: imageUrl,
29061                         id: "delete",
29062                         text: 'Delete'
29063                 });
29064         },
29065         
29066 /**     Return the context menu for this DDView. */
29067         getContextMenu: function() {
29068                 if (!this.contextMenu) {
29069 //                      Create the View's context menu
29070                         this.contextMenu = new Roo.menu.Menu({
29071                                 id: this.id + "-contextmenu"
29072                         });
29073                         this.el.on("contextmenu", this.showContextMenu, this);
29074                 }
29075                 return this.contextMenu;
29076         },
29077         
29078         disableContextMenu: function() {
29079                 if (this.contextMenu) {
29080                         this.el.un("contextmenu", this.showContextMenu, this);
29081                 }
29082         },
29083
29084         showContextMenu: function(e, item) {
29085         item = this.findItemFromChild(e.getTarget());
29086                 if (item) {
29087                         e.stopEvent();
29088                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
29089                         this.contextMenu.showAt(e.getXY());
29090             }
29091     },
29092
29093 /**
29094  *      Remove {@link Roo.data.Record}s at the specified indices.
29095  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
29096  */
29097     remove: function(selectedIndices) {
29098                 selectedIndices = [].concat(selectedIndices);
29099                 for (var i = 0; i < selectedIndices.length; i++) {
29100                         var rec = this.store.getAt(selectedIndices[i]);
29101                         this.store.remove(rec);
29102                 }
29103     },
29104
29105 /**
29106  *      Double click fires the event, but also, if this is draggable, and there is only one other
29107  *      related DropZone, it transfers the selected node.
29108  */
29109     onDblClick : function(e){
29110         var item = this.findItemFromChild(e.getTarget());
29111         if(item){
29112             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
29113                 return false;
29114             }
29115             if (this.dragGroup) {
29116                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
29117                     while (targets.indexOf(this.dropZone) > -1) {
29118                             targets.remove(this.dropZone);
29119                                 }
29120                     if (targets.length == 1) {
29121                                         this.dragZone.cachedTarget = null;
29122                         var el = Roo.get(targets[0].getEl());
29123                         var box = el.getBox(true);
29124                         targets[0].onNodeDrop(el.dom, {
29125                                 target: el.dom,
29126                                 xy: [box.x, box.y + box.height - 1]
29127                         }, null, this.getDragData(e));
29128                     }
29129                 }
29130         }
29131     },
29132     
29133     handleSelection: function(e) {
29134                 this.dragZone.cachedTarget = null;
29135         var item = this.findItemFromChild(e.getTarget());
29136         if (!item) {
29137                 this.clearSelections(true);
29138                 return;
29139         }
29140                 if (item && (this.multiSelect || this.singleSelect)){
29141                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
29142                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
29143                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
29144                                 this.unselect(item);
29145                         } else {
29146                                 this.select(item, this.multiSelect && e.ctrlKey);
29147                                 this.lastSelection = item;
29148                         }
29149                 }
29150     },
29151
29152     onItemClick : function(item, index, e){
29153                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
29154                         return false;
29155                 }
29156                 return true;
29157     },
29158
29159     unselect : function(nodeInfo, suppressEvent){
29160                 var node = this.getNode(nodeInfo);
29161                 if(node && this.isSelected(node)){
29162                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
29163                                 Roo.fly(node).removeClass(this.selectedClass);
29164                                 this.selections.remove(node);
29165                                 if(!suppressEvent){
29166                                         this.fireEvent("selectionchange", this, this.selections);
29167                                 }
29168                         }
29169                 }
29170     }
29171 });
29172 /*
29173  * Based on:
29174  * Ext JS Library 1.1.1
29175  * Copyright(c) 2006-2007, Ext JS, LLC.
29176  *
29177  * Originally Released Under LGPL - original licence link has changed is not relivant.
29178  *
29179  * Fork - LGPL
29180  * <script type="text/javascript">
29181  */
29182  
29183 /**
29184  * @class Roo.LayoutManager
29185  * @extends Roo.util.Observable
29186  * Base class for layout managers.
29187  */
29188 Roo.LayoutManager = function(container, config){
29189     Roo.LayoutManager.superclass.constructor.call(this);
29190     this.el = Roo.get(container);
29191     // ie scrollbar fix
29192     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
29193         document.body.scroll = "no";
29194     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
29195         this.el.position('relative');
29196     }
29197     this.id = this.el.id;
29198     this.el.addClass("x-layout-container");
29199     /** false to disable window resize monitoring @type Boolean */
29200     this.monitorWindowResize = true;
29201     this.regions = {};
29202     this.addEvents({
29203         /**
29204          * @event layout
29205          * Fires when a layout is performed. 
29206          * @param {Roo.LayoutManager} this
29207          */
29208         "layout" : true,
29209         /**
29210          * @event regionresized
29211          * Fires when the user resizes a region. 
29212          * @param {Roo.LayoutRegion} region The resized region
29213          * @param {Number} newSize The new size (width for east/west, height for north/south)
29214          */
29215         "regionresized" : true,
29216         /**
29217          * @event regioncollapsed
29218          * Fires when a region is collapsed. 
29219          * @param {Roo.LayoutRegion} region The collapsed region
29220          */
29221         "regioncollapsed" : true,
29222         /**
29223          * @event regionexpanded
29224          * Fires when a region is expanded.  
29225          * @param {Roo.LayoutRegion} region The expanded region
29226          */
29227         "regionexpanded" : true
29228     });
29229     this.updating = false;
29230     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
29231 };
29232
29233 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
29234     /**
29235      * Returns true if this layout is currently being updated
29236      * @return {Boolean}
29237      */
29238     isUpdating : function(){
29239         return this.updating; 
29240     },
29241     
29242     /**
29243      * Suspend the LayoutManager from doing auto-layouts while
29244      * making multiple add or remove calls
29245      */
29246     beginUpdate : function(){
29247         this.updating = true;    
29248     },
29249     
29250     /**
29251      * Restore auto-layouts and optionally disable the manager from performing a layout
29252      * @param {Boolean} noLayout true to disable a layout update 
29253      */
29254     endUpdate : function(noLayout){
29255         this.updating = false;
29256         if(!noLayout){
29257             this.layout();
29258         }    
29259     },
29260     
29261     layout: function(){
29262         
29263     },
29264     
29265     onRegionResized : function(region, newSize){
29266         this.fireEvent("regionresized", region, newSize);
29267         this.layout();
29268     },
29269     
29270     onRegionCollapsed : function(region){
29271         this.fireEvent("regioncollapsed", region);
29272     },
29273     
29274     onRegionExpanded : function(region){
29275         this.fireEvent("regionexpanded", region);
29276     },
29277         
29278     /**
29279      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
29280      * performs box-model adjustments.
29281      * @return {Object} The size as an object {width: (the width), height: (the height)}
29282      */
29283     getViewSize : function(){
29284         var size;
29285         if(this.el.dom != document.body){
29286             size = this.el.getSize();
29287         }else{
29288             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
29289         }
29290         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
29291         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29292         return size;
29293     },
29294     
29295     /**
29296      * Returns the Element this layout is bound to.
29297      * @return {Roo.Element}
29298      */
29299     getEl : function(){
29300         return this.el;
29301     },
29302     
29303     /**
29304      * Returns the specified region.
29305      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
29306      * @return {Roo.LayoutRegion}
29307      */
29308     getRegion : function(target){
29309         return this.regions[target.toLowerCase()];
29310     },
29311     
29312     onWindowResize : function(){
29313         if(this.monitorWindowResize){
29314             this.layout();
29315         }
29316     }
29317 });/*
29318  * Based on:
29319  * Ext JS Library 1.1.1
29320  * Copyright(c) 2006-2007, Ext JS, LLC.
29321  *
29322  * Originally Released Under LGPL - original licence link has changed is not relivant.
29323  *
29324  * Fork - LGPL
29325  * <script type="text/javascript">
29326  */
29327 /**
29328  * @class Roo.BorderLayout
29329  * @extends Roo.LayoutManager
29330  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
29331  * please see: <br><br>
29332  * <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>
29333  * <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>
29334  * Example:
29335  <pre><code>
29336  var layout = new Roo.BorderLayout(document.body, {
29337     north: {
29338         initialSize: 25,
29339         titlebar: false
29340     },
29341     west: {
29342         split:true,
29343         initialSize: 200,
29344         minSize: 175,
29345         maxSize: 400,
29346         titlebar: true,
29347         collapsible: true
29348     },
29349     east: {
29350         split:true,
29351         initialSize: 202,
29352         minSize: 175,
29353         maxSize: 400,
29354         titlebar: true,
29355         collapsible: true
29356     },
29357     south: {
29358         split:true,
29359         initialSize: 100,
29360         minSize: 100,
29361         maxSize: 200,
29362         titlebar: true,
29363         collapsible: true
29364     },
29365     center: {
29366         titlebar: true,
29367         autoScroll:true,
29368         resizeTabs: true,
29369         minTabWidth: 50,
29370         preferredTabWidth: 150
29371     }
29372 });
29373
29374 // shorthand
29375 var CP = Roo.ContentPanel;
29376
29377 layout.beginUpdate();
29378 layout.add("north", new CP("north", "North"));
29379 layout.add("south", new CP("south", {title: "South", closable: true}));
29380 layout.add("west", new CP("west", {title: "West"}));
29381 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29382 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29383 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29384 layout.getRegion("center").showPanel("center1");
29385 layout.endUpdate();
29386 </code></pre>
29387
29388 <b>The container the layout is rendered into can be either the body element or any other element.
29389 If it is not the body element, the container needs to either be an absolute positioned element,
29390 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29391 the container size if it is not the body element.</b>
29392
29393 * @constructor
29394 * Create a new BorderLayout
29395 * @param {String/HTMLElement/Element} container The container this layout is bound to
29396 * @param {Object} config Configuration options
29397  */
29398 Roo.BorderLayout = function(container, config){
29399     config = config || {};
29400     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29401     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29402     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29403         var target = this.factory.validRegions[i];
29404         if(config[target]){
29405             this.addRegion(target, config[target]);
29406         }
29407     }
29408 };
29409
29410 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29411     /**
29412      * Creates and adds a new region if it doesn't already exist.
29413      * @param {String} target The target region key (north, south, east, west or center).
29414      * @param {Object} config The regions config object
29415      * @return {BorderLayoutRegion} The new region
29416      */
29417     addRegion : function(target, config){
29418         if(!this.regions[target]){
29419             var r = this.factory.create(target, this, config);
29420             this.bindRegion(target, r);
29421         }
29422         return this.regions[target];
29423     },
29424
29425     // private (kinda)
29426     bindRegion : function(name, r){
29427         this.regions[name] = r;
29428         r.on("visibilitychange", this.layout, this);
29429         r.on("paneladded", this.layout, this);
29430         r.on("panelremoved", this.layout, this);
29431         r.on("invalidated", this.layout, this);
29432         r.on("resized", this.onRegionResized, this);
29433         r.on("collapsed", this.onRegionCollapsed, this);
29434         r.on("expanded", this.onRegionExpanded, this);
29435     },
29436
29437     /**
29438      * Performs a layout update.
29439      */
29440     layout : function(){
29441         if(this.updating) {
29442             return;
29443         }
29444         var size = this.getViewSize();
29445         var w = size.width;
29446         var h = size.height;
29447         var centerW = w;
29448         var centerH = h;
29449         var centerY = 0;
29450         var centerX = 0;
29451         //var x = 0, y = 0;
29452
29453         var rs = this.regions;
29454         var north = rs["north"];
29455         var south = rs["south"]; 
29456         var west = rs["west"];
29457         var east = rs["east"];
29458         var center = rs["center"];
29459         //if(this.hideOnLayout){ // not supported anymore
29460             //c.el.setStyle("display", "none");
29461         //}
29462         if(north && north.isVisible()){
29463             var b = north.getBox();
29464             var m = north.getMargins();
29465             b.width = w - (m.left+m.right);
29466             b.x = m.left;
29467             b.y = m.top;
29468             centerY = b.height + b.y + m.bottom;
29469             centerH -= centerY;
29470             north.updateBox(this.safeBox(b));
29471         }
29472         if(south && south.isVisible()){
29473             var b = south.getBox();
29474             var m = south.getMargins();
29475             b.width = w - (m.left+m.right);
29476             b.x = m.left;
29477             var totalHeight = (b.height + m.top + m.bottom);
29478             b.y = h - totalHeight + m.top;
29479             centerH -= totalHeight;
29480             south.updateBox(this.safeBox(b));
29481         }
29482         if(west && west.isVisible()){
29483             var b = west.getBox();
29484             var m = west.getMargins();
29485             b.height = centerH - (m.top+m.bottom);
29486             b.x = m.left;
29487             b.y = centerY + m.top;
29488             var totalWidth = (b.width + m.left + m.right);
29489             centerX += totalWidth;
29490             centerW -= totalWidth;
29491             west.updateBox(this.safeBox(b));
29492         }
29493         if(east && east.isVisible()){
29494             var b = east.getBox();
29495             var m = east.getMargins();
29496             b.height = centerH - (m.top+m.bottom);
29497             var totalWidth = (b.width + m.left + m.right);
29498             b.x = w - totalWidth + m.left;
29499             b.y = centerY + m.top;
29500             centerW -= totalWidth;
29501             east.updateBox(this.safeBox(b));
29502         }
29503         if(center){
29504             var m = center.getMargins();
29505             var centerBox = {
29506                 x: centerX + m.left,
29507                 y: centerY + m.top,
29508                 width: centerW - (m.left+m.right),
29509                 height: centerH - (m.top+m.bottom)
29510             };
29511             //if(this.hideOnLayout){
29512                 //center.el.setStyle("display", "block");
29513             //}
29514             center.updateBox(this.safeBox(centerBox));
29515         }
29516         this.el.repaint();
29517         this.fireEvent("layout", this);
29518     },
29519
29520     // private
29521     safeBox : function(box){
29522         box.width = Math.max(0, box.width);
29523         box.height = Math.max(0, box.height);
29524         return box;
29525     },
29526
29527     /**
29528      * Adds a ContentPanel (or subclass) to this layout.
29529      * @param {String} target The target region key (north, south, east, west or center).
29530      * @param {Roo.ContentPanel} panel The panel to add
29531      * @return {Roo.ContentPanel} The added panel
29532      */
29533     add : function(target, panel){
29534          
29535         target = target.toLowerCase();
29536         return this.regions[target].add(panel);
29537     },
29538
29539     /**
29540      * Remove a ContentPanel (or subclass) to this layout.
29541      * @param {String} target The target region key (north, south, east, west or center).
29542      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29543      * @return {Roo.ContentPanel} The removed panel
29544      */
29545     remove : function(target, panel){
29546         target = target.toLowerCase();
29547         return this.regions[target].remove(panel);
29548     },
29549
29550     /**
29551      * Searches all regions for a panel with the specified id
29552      * @param {String} panelId
29553      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29554      */
29555     findPanel : function(panelId){
29556         var rs = this.regions;
29557         for(var target in rs){
29558             if(typeof rs[target] != "function"){
29559                 var p = rs[target].getPanel(panelId);
29560                 if(p){
29561                     return p;
29562                 }
29563             }
29564         }
29565         return null;
29566     },
29567
29568     /**
29569      * Searches all regions for a panel with the specified id and activates (shows) it.
29570      * @param {String/ContentPanel} panelId The panels id or the panel itself
29571      * @return {Roo.ContentPanel} The shown panel or null
29572      */
29573     showPanel : function(panelId) {
29574       var rs = this.regions;
29575       for(var target in rs){
29576          var r = rs[target];
29577          if(typeof r != "function"){
29578             if(r.hasPanel(panelId)){
29579                return r.showPanel(panelId);
29580             }
29581          }
29582       }
29583       return null;
29584    },
29585
29586    /**
29587      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29588      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29589      */
29590     restoreState : function(provider){
29591         if(!provider){
29592             provider = Roo.state.Manager;
29593         }
29594         var sm = new Roo.LayoutStateManager();
29595         sm.init(this, provider);
29596     },
29597
29598     /**
29599      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29600      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29601      * a valid ContentPanel config object.  Example:
29602      * <pre><code>
29603 // Create the main layout
29604 var layout = new Roo.BorderLayout('main-ct', {
29605     west: {
29606         split:true,
29607         minSize: 175,
29608         titlebar: true
29609     },
29610     center: {
29611         title:'Components'
29612     }
29613 }, 'main-ct');
29614
29615 // Create and add multiple ContentPanels at once via configs
29616 layout.batchAdd({
29617    west: {
29618        id: 'source-files',
29619        autoCreate:true,
29620        title:'Ext Source Files',
29621        autoScroll:true,
29622        fitToFrame:true
29623    },
29624    center : {
29625        el: cview,
29626        autoScroll:true,
29627        fitToFrame:true,
29628        toolbar: tb,
29629        resizeEl:'cbody'
29630    }
29631 });
29632 </code></pre>
29633      * @param {Object} regions An object containing ContentPanel configs by region name
29634      */
29635     batchAdd : function(regions){
29636         this.beginUpdate();
29637         for(var rname in regions){
29638             var lr = this.regions[rname];
29639             if(lr){
29640                 this.addTypedPanels(lr, regions[rname]);
29641             }
29642         }
29643         this.endUpdate();
29644     },
29645
29646     // private
29647     addTypedPanels : function(lr, ps){
29648         if(typeof ps == 'string'){
29649             lr.add(new Roo.ContentPanel(ps));
29650         }
29651         else if(ps instanceof Array){
29652             for(var i =0, len = ps.length; i < len; i++){
29653                 this.addTypedPanels(lr, ps[i]);
29654             }
29655         }
29656         else if(!ps.events){ // raw config?
29657             var el = ps.el;
29658             delete ps.el; // prevent conflict
29659             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29660         }
29661         else {  // panel object assumed!
29662             lr.add(ps);
29663         }
29664     },
29665     /**
29666      * Adds a xtype elements to the layout.
29667      * <pre><code>
29668
29669 layout.addxtype({
29670        xtype : 'ContentPanel',
29671        region: 'west',
29672        items: [ .... ]
29673    }
29674 );
29675
29676 layout.addxtype({
29677         xtype : 'NestedLayoutPanel',
29678         region: 'west',
29679         layout: {
29680            center: { },
29681            west: { }   
29682         },
29683         items : [ ... list of content panels or nested layout panels.. ]
29684    }
29685 );
29686 </code></pre>
29687      * @param {Object} cfg Xtype definition of item to add.
29688      */
29689     addxtype : function(cfg)
29690     {
29691         // basically accepts a pannel...
29692         // can accept a layout region..!?!?
29693         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29694         
29695         if (!cfg.xtype.match(/Panel$/)) {
29696             return false;
29697         }
29698         var ret = false;
29699         
29700         if (typeof(cfg.region) == 'undefined') {
29701             Roo.log("Failed to add Panel, region was not set");
29702             Roo.log(cfg);
29703             return false;
29704         }
29705         var region = cfg.region;
29706         delete cfg.region;
29707         
29708           
29709         var xitems = [];
29710         if (cfg.items) {
29711             xitems = cfg.items;
29712             delete cfg.items;
29713         }
29714         var nb = false;
29715         
29716         switch(cfg.xtype) 
29717         {
29718             case 'ContentPanel':  // ContentPanel (el, cfg)
29719             case 'ScrollPanel':  // ContentPanel (el, cfg)
29720             case 'ViewPanel': 
29721                 if(cfg.autoCreate) {
29722                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29723                 } else {
29724                     var el = this.el.createChild();
29725                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29726                 }
29727                 
29728                 this.add(region, ret);
29729                 break;
29730             
29731             
29732             case 'TreePanel': // our new panel!
29733                 cfg.el = this.el.createChild();
29734                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29735                 this.add(region, ret);
29736                 break;
29737             
29738             case 'NestedLayoutPanel': 
29739                 // create a new Layout (which is  a Border Layout...
29740                 var el = this.el.createChild();
29741                 var clayout = cfg.layout;
29742                 delete cfg.layout;
29743                 clayout.items   = clayout.items  || [];
29744                 // replace this exitems with the clayout ones..
29745                 xitems = clayout.items;
29746                  
29747                 
29748                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29749                     cfg.background = false;
29750                 }
29751                 var layout = new Roo.BorderLayout(el, clayout);
29752                 
29753                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29754                 //console.log('adding nested layout panel '  + cfg.toSource());
29755                 this.add(region, ret);
29756                 nb = {}; /// find first...
29757                 break;
29758                 
29759             case 'GridPanel': 
29760             
29761                 // needs grid and region
29762                 
29763                 //var el = this.getRegion(region).el.createChild();
29764                 var el = this.el.createChild();
29765                 // create the grid first...
29766                 
29767                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29768                 delete cfg.grid;
29769                 if (region == 'center' && this.active ) {
29770                     cfg.background = false;
29771                 }
29772                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29773                 
29774                 this.add(region, ret);
29775                 if (cfg.background) {
29776                     ret.on('activate', function(gp) {
29777                         if (!gp.grid.rendered) {
29778                             gp.grid.render();
29779                         }
29780                     });
29781                 } else {
29782                     grid.render();
29783                 }
29784                 break;
29785            
29786            
29787            
29788                 
29789                 
29790                 
29791             default:
29792                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29793                     
29794                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29795                     this.add(region, ret);
29796                 } else {
29797                 
29798                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29799                     return null;
29800                 }
29801                 
29802              // GridPanel (grid, cfg)
29803             
29804         }
29805         this.beginUpdate();
29806         // add children..
29807         var region = '';
29808         var abn = {};
29809         Roo.each(xitems, function(i)  {
29810             region = nb && i.region ? i.region : false;
29811             
29812             var add = ret.addxtype(i);
29813            
29814             if (region) {
29815                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29816                 if (!i.background) {
29817                     abn[region] = nb[region] ;
29818                 }
29819             }
29820             
29821         });
29822         this.endUpdate();
29823
29824         // make the last non-background panel active..
29825         //if (nb) { Roo.log(abn); }
29826         if (nb) {
29827             
29828             for(var r in abn) {
29829                 region = this.getRegion(r);
29830                 if (region) {
29831                     // tried using nb[r], but it does not work..
29832                      
29833                     region.showPanel(abn[r]);
29834                    
29835                 }
29836             }
29837         }
29838         return ret;
29839         
29840     }
29841 });
29842
29843 /**
29844  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29845  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29846  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29847  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29848  * <pre><code>
29849 // shorthand
29850 var CP = Roo.ContentPanel;
29851
29852 var layout = Roo.BorderLayout.create({
29853     north: {
29854         initialSize: 25,
29855         titlebar: false,
29856         panels: [new CP("north", "North")]
29857     },
29858     west: {
29859         split:true,
29860         initialSize: 200,
29861         minSize: 175,
29862         maxSize: 400,
29863         titlebar: true,
29864         collapsible: true,
29865         panels: [new CP("west", {title: "West"})]
29866     },
29867     east: {
29868         split:true,
29869         initialSize: 202,
29870         minSize: 175,
29871         maxSize: 400,
29872         titlebar: true,
29873         collapsible: true,
29874         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29875     },
29876     south: {
29877         split:true,
29878         initialSize: 100,
29879         minSize: 100,
29880         maxSize: 200,
29881         titlebar: true,
29882         collapsible: true,
29883         panels: [new CP("south", {title: "South", closable: true})]
29884     },
29885     center: {
29886         titlebar: true,
29887         autoScroll:true,
29888         resizeTabs: true,
29889         minTabWidth: 50,
29890         preferredTabWidth: 150,
29891         panels: [
29892             new CP("center1", {title: "Close Me", closable: true}),
29893             new CP("center2", {title: "Center Panel", closable: false})
29894         ]
29895     }
29896 }, document.body);
29897
29898 layout.getRegion("center").showPanel("center1");
29899 </code></pre>
29900  * @param config
29901  * @param targetEl
29902  */
29903 Roo.BorderLayout.create = function(config, targetEl){
29904     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29905     layout.beginUpdate();
29906     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29907     for(var j = 0, jlen = regions.length; j < jlen; j++){
29908         var lr = regions[j];
29909         if(layout.regions[lr] && config[lr].panels){
29910             var r = layout.regions[lr];
29911             var ps = config[lr].panels;
29912             layout.addTypedPanels(r, ps);
29913         }
29914     }
29915     layout.endUpdate();
29916     return layout;
29917 };
29918
29919 // private
29920 Roo.BorderLayout.RegionFactory = {
29921     // private
29922     validRegions : ["north","south","east","west","center"],
29923
29924     // private
29925     create : function(target, mgr, config){
29926         target = target.toLowerCase();
29927         if(config.lightweight || config.basic){
29928             return new Roo.BasicLayoutRegion(mgr, config, target);
29929         }
29930         switch(target){
29931             case "north":
29932                 return new Roo.NorthLayoutRegion(mgr, config);
29933             case "south":
29934                 return new Roo.SouthLayoutRegion(mgr, config);
29935             case "east":
29936                 return new Roo.EastLayoutRegion(mgr, config);
29937             case "west":
29938                 return new Roo.WestLayoutRegion(mgr, config);
29939             case "center":
29940                 return new Roo.CenterLayoutRegion(mgr, config);
29941         }
29942         throw 'Layout region "'+target+'" not supported.';
29943     }
29944 };/*
29945  * Based on:
29946  * Ext JS Library 1.1.1
29947  * Copyright(c) 2006-2007, Ext JS, LLC.
29948  *
29949  * Originally Released Under LGPL - original licence link has changed is not relivant.
29950  *
29951  * Fork - LGPL
29952  * <script type="text/javascript">
29953  */
29954  
29955 /**
29956  * @class Roo.BasicLayoutRegion
29957  * @extends Roo.util.Observable
29958  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29959  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29960  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29961  */
29962 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29963     this.mgr = mgr;
29964     this.position  = pos;
29965     this.events = {
29966         /**
29967          * @scope Roo.BasicLayoutRegion
29968          */
29969         
29970         /**
29971          * @event beforeremove
29972          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29973          * @param {Roo.LayoutRegion} this
29974          * @param {Roo.ContentPanel} panel The panel
29975          * @param {Object} e The cancel event object
29976          */
29977         "beforeremove" : true,
29978         /**
29979          * @event invalidated
29980          * Fires when the layout for this region is changed.
29981          * @param {Roo.LayoutRegion} this
29982          */
29983         "invalidated" : true,
29984         /**
29985          * @event visibilitychange
29986          * Fires when this region is shown or hidden 
29987          * @param {Roo.LayoutRegion} this
29988          * @param {Boolean} visibility true or false
29989          */
29990         "visibilitychange" : true,
29991         /**
29992          * @event paneladded
29993          * Fires when a panel is added. 
29994          * @param {Roo.LayoutRegion} this
29995          * @param {Roo.ContentPanel} panel The panel
29996          */
29997         "paneladded" : true,
29998         /**
29999          * @event panelremoved
30000          * Fires when a panel is removed. 
30001          * @param {Roo.LayoutRegion} this
30002          * @param {Roo.ContentPanel} panel The panel
30003          */
30004         "panelremoved" : true,
30005         /**
30006          * @event beforecollapse
30007          * Fires when this region before collapse.
30008          * @param {Roo.LayoutRegion} this
30009          */
30010         "beforecollapse" : true,
30011         /**
30012          * @event collapsed
30013          * Fires when this region is collapsed.
30014          * @param {Roo.LayoutRegion} this
30015          */
30016         "collapsed" : true,
30017         /**
30018          * @event expanded
30019          * Fires when this region is expanded.
30020          * @param {Roo.LayoutRegion} this
30021          */
30022         "expanded" : true,
30023         /**
30024          * @event slideshow
30025          * Fires when this region is slid into view.
30026          * @param {Roo.LayoutRegion} this
30027          */
30028         "slideshow" : true,
30029         /**
30030          * @event slidehide
30031          * Fires when this region slides out of view. 
30032          * @param {Roo.LayoutRegion} this
30033          */
30034         "slidehide" : true,
30035         /**
30036          * @event panelactivated
30037          * Fires when a panel is activated. 
30038          * @param {Roo.LayoutRegion} this
30039          * @param {Roo.ContentPanel} panel The activated panel
30040          */
30041         "panelactivated" : true,
30042         /**
30043          * @event resized
30044          * Fires when the user resizes this region. 
30045          * @param {Roo.LayoutRegion} this
30046          * @param {Number} newSize The new size (width for east/west, height for north/south)
30047          */
30048         "resized" : true
30049     };
30050     /** A collection of panels in this region. @type Roo.util.MixedCollection */
30051     this.panels = new Roo.util.MixedCollection();
30052     this.panels.getKey = this.getPanelId.createDelegate(this);
30053     this.box = null;
30054     this.activePanel = null;
30055     // ensure listeners are added...
30056     
30057     if (config.listeners || config.events) {
30058         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
30059             listeners : config.listeners || {},
30060             events : config.events || {}
30061         });
30062     }
30063     
30064     if(skipConfig !== true){
30065         this.applyConfig(config);
30066     }
30067 };
30068
30069 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
30070     getPanelId : function(p){
30071         return p.getId();
30072     },
30073     
30074     applyConfig : function(config){
30075         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30076         this.config = config;
30077         
30078     },
30079     
30080     /**
30081      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
30082      * the width, for horizontal (north, south) the height.
30083      * @param {Number} newSize The new width or height
30084      */
30085     resizeTo : function(newSize){
30086         var el = this.el ? this.el :
30087                  (this.activePanel ? this.activePanel.getEl() : null);
30088         if(el){
30089             switch(this.position){
30090                 case "east":
30091                 case "west":
30092                     el.setWidth(newSize);
30093                     this.fireEvent("resized", this, newSize);
30094                 break;
30095                 case "north":
30096                 case "south":
30097                     el.setHeight(newSize);
30098                     this.fireEvent("resized", this, newSize);
30099                 break;                
30100             }
30101         }
30102     },
30103     
30104     getBox : function(){
30105         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
30106     },
30107     
30108     getMargins : function(){
30109         return this.margins;
30110     },
30111     
30112     updateBox : function(box){
30113         this.box = box;
30114         var el = this.activePanel.getEl();
30115         el.dom.style.left = box.x + "px";
30116         el.dom.style.top = box.y + "px";
30117         this.activePanel.setSize(box.width, box.height);
30118     },
30119     
30120     /**
30121      * Returns the container element for this region.
30122      * @return {Roo.Element}
30123      */
30124     getEl : function(){
30125         return this.activePanel;
30126     },
30127     
30128     /**
30129      * Returns true if this region is currently visible.
30130      * @return {Boolean}
30131      */
30132     isVisible : function(){
30133         return this.activePanel ? true : false;
30134     },
30135     
30136     setActivePanel : function(panel){
30137         panel = this.getPanel(panel);
30138         if(this.activePanel && this.activePanel != panel){
30139             this.activePanel.setActiveState(false);
30140             this.activePanel.getEl().setLeftTop(-10000,-10000);
30141         }
30142         this.activePanel = panel;
30143         panel.setActiveState(true);
30144         if(this.box){
30145             panel.setSize(this.box.width, this.box.height);
30146         }
30147         this.fireEvent("panelactivated", this, panel);
30148         this.fireEvent("invalidated");
30149     },
30150     
30151     /**
30152      * Show the specified panel.
30153      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
30154      * @return {Roo.ContentPanel} The shown panel or null
30155      */
30156     showPanel : function(panel){
30157         if(panel = this.getPanel(panel)){
30158             this.setActivePanel(panel);
30159         }
30160         return panel;
30161     },
30162     
30163     /**
30164      * Get the active panel for this region.
30165      * @return {Roo.ContentPanel} The active panel or null
30166      */
30167     getActivePanel : function(){
30168         return this.activePanel;
30169     },
30170     
30171     /**
30172      * Add the passed ContentPanel(s)
30173      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30174      * @return {Roo.ContentPanel} The panel added (if only one was added)
30175      */
30176     add : function(panel){
30177         if(arguments.length > 1){
30178             for(var i = 0, len = arguments.length; i < len; i++) {
30179                 this.add(arguments[i]);
30180             }
30181             return null;
30182         }
30183         if(this.hasPanel(panel)){
30184             this.showPanel(panel);
30185             return panel;
30186         }
30187         var el = panel.getEl();
30188         if(el.dom.parentNode != this.mgr.el.dom){
30189             this.mgr.el.dom.appendChild(el.dom);
30190         }
30191         if(panel.setRegion){
30192             panel.setRegion(this);
30193         }
30194         this.panels.add(panel);
30195         el.setStyle("position", "absolute");
30196         if(!panel.background){
30197             this.setActivePanel(panel);
30198             if(this.config.initialSize && this.panels.getCount()==1){
30199                 this.resizeTo(this.config.initialSize);
30200             }
30201         }
30202         this.fireEvent("paneladded", this, panel);
30203         return panel;
30204     },
30205     
30206     /**
30207      * Returns true if the panel is in this region.
30208      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30209      * @return {Boolean}
30210      */
30211     hasPanel : function(panel){
30212         if(typeof panel == "object"){ // must be panel obj
30213             panel = panel.getId();
30214         }
30215         return this.getPanel(panel) ? true : false;
30216     },
30217     
30218     /**
30219      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30220      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30221      * @param {Boolean} preservePanel Overrides the config preservePanel option
30222      * @return {Roo.ContentPanel} The panel that was removed
30223      */
30224     remove : function(panel, preservePanel){
30225         panel = this.getPanel(panel);
30226         if(!panel){
30227             return null;
30228         }
30229         var e = {};
30230         this.fireEvent("beforeremove", this, panel, e);
30231         if(e.cancel === true){
30232             return null;
30233         }
30234         var panelId = panel.getId();
30235         this.panels.removeKey(panelId);
30236         return panel;
30237     },
30238     
30239     /**
30240      * Returns the panel specified or null if it's not in this region.
30241      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
30242      * @return {Roo.ContentPanel}
30243      */
30244     getPanel : function(id){
30245         if(typeof id == "object"){ // must be panel obj
30246             return id;
30247         }
30248         return this.panels.get(id);
30249     },
30250     
30251     /**
30252      * Returns this regions position (north/south/east/west/center).
30253      * @return {String} 
30254      */
30255     getPosition: function(){
30256         return this.position;    
30257     }
30258 });/*
30259  * Based on:
30260  * Ext JS Library 1.1.1
30261  * Copyright(c) 2006-2007, Ext JS, LLC.
30262  *
30263  * Originally Released Under LGPL - original licence link has changed is not relivant.
30264  *
30265  * Fork - LGPL
30266  * <script type="text/javascript">
30267  */
30268  
30269 /**
30270  * @class Roo.LayoutRegion
30271  * @extends Roo.BasicLayoutRegion
30272  * This class represents a region in a layout manager.
30273  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
30274  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
30275  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
30276  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
30277  * @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})
30278  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
30279  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
30280  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
30281  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
30282  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
30283  * @cfg {String}    title           The title for the region (overrides panel titles)
30284  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
30285  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
30286  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
30287  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
30288  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
30289  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
30290  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
30291  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
30292  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
30293  * @cfg {Boolean}   showPin         True to show a pin button
30294  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
30295  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
30296  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
30297  * @cfg {Number}    width           For East/West panels
30298  * @cfg {Number}    height          For North/South panels
30299  * @cfg {Boolean}   split           To show the splitter
30300  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
30301  */
30302 Roo.LayoutRegion = function(mgr, config, pos){
30303     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
30304     var dh = Roo.DomHelper;
30305     /** This region's container element 
30306     * @type Roo.Element */
30307     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
30308     /** This region's title element 
30309     * @type Roo.Element */
30310
30311     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
30312         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
30313         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
30314     ]}, true);
30315     this.titleEl.enableDisplayMode();
30316     /** This region's title text element 
30317     * @type HTMLElement */
30318     this.titleTextEl = this.titleEl.dom.firstChild;
30319     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
30320     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
30321     this.closeBtn.enableDisplayMode();
30322     this.closeBtn.on("click", this.closeClicked, this);
30323     this.closeBtn.hide();
30324
30325     this.createBody(config);
30326     this.visible = true;
30327     this.collapsed = false;
30328
30329     if(config.hideWhenEmpty){
30330         this.hide();
30331         this.on("paneladded", this.validateVisibility, this);
30332         this.on("panelremoved", this.validateVisibility, this);
30333     }
30334     this.applyConfig(config);
30335 };
30336
30337 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30338
30339     createBody : function(){
30340         /** This region's body element 
30341         * @type Roo.Element */
30342         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30343     },
30344
30345     applyConfig : function(c){
30346         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30347             var dh = Roo.DomHelper;
30348             if(c.titlebar !== false){
30349                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30350                 this.collapseBtn.on("click", this.collapse, this);
30351                 this.collapseBtn.enableDisplayMode();
30352
30353                 if(c.showPin === true || this.showPin){
30354                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30355                     this.stickBtn.enableDisplayMode();
30356                     this.stickBtn.on("click", this.expand, this);
30357                     this.stickBtn.hide();
30358                 }
30359             }
30360             /** This region's collapsed element
30361             * @type Roo.Element */
30362             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30363                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30364             ]}, true);
30365             if(c.floatable !== false){
30366                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30367                this.collapsedEl.on("click", this.collapseClick, this);
30368             }
30369
30370             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30371                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30372                    id: "message", unselectable: "on", style:{"float":"left"}});
30373                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30374              }
30375             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30376             this.expandBtn.on("click", this.expand, this);
30377         }
30378         if(this.collapseBtn){
30379             this.collapseBtn.setVisible(c.collapsible == true);
30380         }
30381         this.cmargins = c.cmargins || this.cmargins ||
30382                          (this.position == "west" || this.position == "east" ?
30383                              {top: 0, left: 2, right:2, bottom: 0} :
30384                              {top: 2, left: 0, right:0, bottom: 2});
30385         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30386         this.bottomTabs = c.tabPosition != "top";
30387         this.autoScroll = c.autoScroll || false;
30388         if(this.autoScroll){
30389             this.bodyEl.setStyle("overflow", "auto");
30390         }else{
30391             this.bodyEl.setStyle("overflow", "hidden");
30392         }
30393         //if(c.titlebar !== false){
30394             if((!c.titlebar && !c.title) || c.titlebar === false){
30395                 this.titleEl.hide();
30396             }else{
30397                 this.titleEl.show();
30398                 if(c.title){
30399                     this.titleTextEl.innerHTML = c.title;
30400                 }
30401             }
30402         //}
30403         this.duration = c.duration || .30;
30404         this.slideDuration = c.slideDuration || .45;
30405         this.config = c;
30406         if(c.collapsed){
30407             this.collapse(true);
30408         }
30409         if(c.hidden){
30410             this.hide();
30411         }
30412     },
30413     /**
30414      * Returns true if this region is currently visible.
30415      * @return {Boolean}
30416      */
30417     isVisible : function(){
30418         return this.visible;
30419     },
30420
30421     /**
30422      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30423      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30424      */
30425     setCollapsedTitle : function(title){
30426         title = title || "&#160;";
30427         if(this.collapsedTitleTextEl){
30428             this.collapsedTitleTextEl.innerHTML = title;
30429         }
30430     },
30431
30432     getBox : function(){
30433         var b;
30434         if(!this.collapsed){
30435             b = this.el.getBox(false, true);
30436         }else{
30437             b = this.collapsedEl.getBox(false, true);
30438         }
30439         return b;
30440     },
30441
30442     getMargins : function(){
30443         return this.collapsed ? this.cmargins : this.margins;
30444     },
30445
30446     highlight : function(){
30447         this.el.addClass("x-layout-panel-dragover");
30448     },
30449
30450     unhighlight : function(){
30451         this.el.removeClass("x-layout-panel-dragover");
30452     },
30453
30454     updateBox : function(box){
30455         this.box = box;
30456         if(!this.collapsed){
30457             this.el.dom.style.left = box.x + "px";
30458             this.el.dom.style.top = box.y + "px";
30459             this.updateBody(box.width, box.height);
30460         }else{
30461             this.collapsedEl.dom.style.left = box.x + "px";
30462             this.collapsedEl.dom.style.top = box.y + "px";
30463             this.collapsedEl.setSize(box.width, box.height);
30464         }
30465         if(this.tabs){
30466             this.tabs.autoSizeTabs();
30467         }
30468     },
30469
30470     updateBody : function(w, h){
30471         if(w !== null){
30472             this.el.setWidth(w);
30473             w -= this.el.getBorderWidth("rl");
30474             if(this.config.adjustments){
30475                 w += this.config.adjustments[0];
30476             }
30477         }
30478         if(h !== null){
30479             this.el.setHeight(h);
30480             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30481             h -= this.el.getBorderWidth("tb");
30482             if(this.config.adjustments){
30483                 h += this.config.adjustments[1];
30484             }
30485             this.bodyEl.setHeight(h);
30486             if(this.tabs){
30487                 h = this.tabs.syncHeight(h);
30488             }
30489         }
30490         if(this.panelSize){
30491             w = w !== null ? w : this.panelSize.width;
30492             h = h !== null ? h : this.panelSize.height;
30493         }
30494         if(this.activePanel){
30495             var el = this.activePanel.getEl();
30496             w = w !== null ? w : el.getWidth();
30497             h = h !== null ? h : el.getHeight();
30498             this.panelSize = {width: w, height: h};
30499             this.activePanel.setSize(w, h);
30500         }
30501         if(Roo.isIE && this.tabs){
30502             this.tabs.el.repaint();
30503         }
30504     },
30505
30506     /**
30507      * Returns the container element for this region.
30508      * @return {Roo.Element}
30509      */
30510     getEl : function(){
30511         return this.el;
30512     },
30513
30514     /**
30515      * Hides this region.
30516      */
30517     hide : function(){
30518         if(!this.collapsed){
30519             this.el.dom.style.left = "-2000px";
30520             this.el.hide();
30521         }else{
30522             this.collapsedEl.dom.style.left = "-2000px";
30523             this.collapsedEl.hide();
30524         }
30525         this.visible = false;
30526         this.fireEvent("visibilitychange", this, false);
30527     },
30528
30529     /**
30530      * Shows this region if it was previously hidden.
30531      */
30532     show : function(){
30533         if(!this.collapsed){
30534             this.el.show();
30535         }else{
30536             this.collapsedEl.show();
30537         }
30538         this.visible = true;
30539         this.fireEvent("visibilitychange", this, true);
30540     },
30541
30542     closeClicked : function(){
30543         if(this.activePanel){
30544             this.remove(this.activePanel);
30545         }
30546     },
30547
30548     collapseClick : function(e){
30549         if(this.isSlid){
30550            e.stopPropagation();
30551            this.slideIn();
30552         }else{
30553            e.stopPropagation();
30554            this.slideOut();
30555         }
30556     },
30557
30558     /**
30559      * Collapses this region.
30560      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30561      */
30562     collapse : function(skipAnim, skipCheck){
30563         if(this.collapsed) {
30564             return;
30565         }
30566         
30567         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30568             
30569             this.collapsed = true;
30570             if(this.split){
30571                 this.split.el.hide();
30572             }
30573             if(this.config.animate && skipAnim !== true){
30574                 this.fireEvent("invalidated", this);
30575                 this.animateCollapse();
30576             }else{
30577                 this.el.setLocation(-20000,-20000);
30578                 this.el.hide();
30579                 this.collapsedEl.show();
30580                 this.fireEvent("collapsed", this);
30581                 this.fireEvent("invalidated", this);
30582             }
30583         }
30584         
30585     },
30586
30587     animateCollapse : function(){
30588         // overridden
30589     },
30590
30591     /**
30592      * Expands this region if it was previously collapsed.
30593      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30594      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30595      */
30596     expand : function(e, skipAnim){
30597         if(e) {
30598             e.stopPropagation();
30599         }
30600         if(!this.collapsed || this.el.hasActiveFx()) {
30601             return;
30602         }
30603         if(this.isSlid){
30604             this.afterSlideIn();
30605             skipAnim = true;
30606         }
30607         this.collapsed = false;
30608         if(this.config.animate && skipAnim !== true){
30609             this.animateExpand();
30610         }else{
30611             this.el.show();
30612             if(this.split){
30613                 this.split.el.show();
30614             }
30615             this.collapsedEl.setLocation(-2000,-2000);
30616             this.collapsedEl.hide();
30617             this.fireEvent("invalidated", this);
30618             this.fireEvent("expanded", this);
30619         }
30620     },
30621
30622     animateExpand : function(){
30623         // overridden
30624     },
30625
30626     initTabs : function()
30627     {
30628         this.bodyEl.setStyle("overflow", "hidden");
30629         var ts = new Roo.TabPanel(
30630                 this.bodyEl.dom,
30631                 {
30632                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30633                     disableTooltips: this.config.disableTabTips,
30634                     toolbar : this.config.toolbar
30635                 }
30636         );
30637         if(this.config.hideTabs){
30638             ts.stripWrap.setDisplayed(false);
30639         }
30640         this.tabs = ts;
30641         ts.resizeTabs = this.config.resizeTabs === true;
30642         ts.minTabWidth = this.config.minTabWidth || 40;
30643         ts.maxTabWidth = this.config.maxTabWidth || 250;
30644         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30645         ts.monitorResize = false;
30646         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30647         ts.bodyEl.addClass('x-layout-tabs-body');
30648         this.panels.each(this.initPanelAsTab, this);
30649     },
30650
30651     initPanelAsTab : function(panel){
30652         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30653                     this.config.closeOnTab && panel.isClosable());
30654         if(panel.tabTip !== undefined){
30655             ti.setTooltip(panel.tabTip);
30656         }
30657         ti.on("activate", function(){
30658               this.setActivePanel(panel);
30659         }, this);
30660         if(this.config.closeOnTab){
30661             ti.on("beforeclose", function(t, e){
30662                 e.cancel = true;
30663                 this.remove(panel);
30664             }, this);
30665         }
30666         return ti;
30667     },
30668
30669     updatePanelTitle : function(panel, title){
30670         if(this.activePanel == panel){
30671             this.updateTitle(title);
30672         }
30673         if(this.tabs){
30674             var ti = this.tabs.getTab(panel.getEl().id);
30675             ti.setText(title);
30676             if(panel.tabTip !== undefined){
30677                 ti.setTooltip(panel.tabTip);
30678             }
30679         }
30680     },
30681
30682     updateTitle : function(title){
30683         if(this.titleTextEl && !this.config.title){
30684             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30685         }
30686     },
30687
30688     setActivePanel : function(panel){
30689         panel = this.getPanel(panel);
30690         if(this.activePanel && this.activePanel != panel){
30691             this.activePanel.setActiveState(false);
30692         }
30693         this.activePanel = panel;
30694         panel.setActiveState(true);
30695         if(this.panelSize){
30696             panel.setSize(this.panelSize.width, this.panelSize.height);
30697         }
30698         if(this.closeBtn){
30699             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30700         }
30701         this.updateTitle(panel.getTitle());
30702         if(this.tabs){
30703             this.fireEvent("invalidated", this);
30704         }
30705         this.fireEvent("panelactivated", this, panel);
30706     },
30707
30708     /**
30709      * Shows the specified panel.
30710      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30711      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30712      */
30713     showPanel : function(panel)
30714     {
30715         panel = this.getPanel(panel);
30716         if(panel){
30717             if(this.tabs){
30718                 var tab = this.tabs.getTab(panel.getEl().id);
30719                 if(tab.isHidden()){
30720                     this.tabs.unhideTab(tab.id);
30721                 }
30722                 tab.activate();
30723             }else{
30724                 this.setActivePanel(panel);
30725             }
30726         }
30727         return panel;
30728     },
30729
30730     /**
30731      * Get the active panel for this region.
30732      * @return {Roo.ContentPanel} The active panel or null
30733      */
30734     getActivePanel : function(){
30735         return this.activePanel;
30736     },
30737
30738     validateVisibility : function(){
30739         if(this.panels.getCount() < 1){
30740             this.updateTitle("&#160;");
30741             this.closeBtn.hide();
30742             this.hide();
30743         }else{
30744             if(!this.isVisible()){
30745                 this.show();
30746             }
30747         }
30748     },
30749
30750     /**
30751      * Adds the passed ContentPanel(s) to this region.
30752      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30753      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30754      */
30755     add : function(panel){
30756         if(arguments.length > 1){
30757             for(var i = 0, len = arguments.length; i < len; i++) {
30758                 this.add(arguments[i]);
30759             }
30760             return null;
30761         }
30762         if(this.hasPanel(panel)){
30763             this.showPanel(panel);
30764             return panel;
30765         }
30766         panel.setRegion(this);
30767         this.panels.add(panel);
30768         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30769             this.bodyEl.dom.appendChild(panel.getEl().dom);
30770             if(panel.background !== true){
30771                 this.setActivePanel(panel);
30772             }
30773             this.fireEvent("paneladded", this, panel);
30774             return panel;
30775         }
30776         if(!this.tabs){
30777             this.initTabs();
30778         }else{
30779             this.initPanelAsTab(panel);
30780         }
30781         if(panel.background !== true){
30782             this.tabs.activate(panel.getEl().id);
30783         }
30784         this.fireEvent("paneladded", this, panel);
30785         return panel;
30786     },
30787
30788     /**
30789      * Hides the tab for the specified panel.
30790      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30791      */
30792     hidePanel : function(panel){
30793         if(this.tabs && (panel = this.getPanel(panel))){
30794             this.tabs.hideTab(panel.getEl().id);
30795         }
30796     },
30797
30798     /**
30799      * Unhides the tab for a previously hidden panel.
30800      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30801      */
30802     unhidePanel : function(panel){
30803         if(this.tabs && (panel = this.getPanel(panel))){
30804             this.tabs.unhideTab(panel.getEl().id);
30805         }
30806     },
30807
30808     clearPanels : function(){
30809         while(this.panels.getCount() > 0){
30810              this.remove(this.panels.first());
30811         }
30812     },
30813
30814     /**
30815      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30816      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30817      * @param {Boolean} preservePanel Overrides the config preservePanel option
30818      * @return {Roo.ContentPanel} The panel that was removed
30819      */
30820     remove : function(panel, preservePanel){
30821         panel = this.getPanel(panel);
30822         if(!panel){
30823             return null;
30824         }
30825         var e = {};
30826         this.fireEvent("beforeremove", this, panel, e);
30827         if(e.cancel === true){
30828             return null;
30829         }
30830         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30831         var panelId = panel.getId();
30832         this.panels.removeKey(panelId);
30833         if(preservePanel){
30834             document.body.appendChild(panel.getEl().dom);
30835         }
30836         if(this.tabs){
30837             this.tabs.removeTab(panel.getEl().id);
30838         }else if (!preservePanel){
30839             this.bodyEl.dom.removeChild(panel.getEl().dom);
30840         }
30841         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30842             var p = this.panels.first();
30843             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30844             tempEl.appendChild(p.getEl().dom);
30845             this.bodyEl.update("");
30846             this.bodyEl.dom.appendChild(p.getEl().dom);
30847             tempEl = null;
30848             this.updateTitle(p.getTitle());
30849             this.tabs = null;
30850             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30851             this.setActivePanel(p);
30852         }
30853         panel.setRegion(null);
30854         if(this.activePanel == panel){
30855             this.activePanel = null;
30856         }
30857         if(this.config.autoDestroy !== false && preservePanel !== true){
30858             try{panel.destroy();}catch(e){}
30859         }
30860         this.fireEvent("panelremoved", this, panel);
30861         return panel;
30862     },
30863
30864     /**
30865      * Returns the TabPanel component used by this region
30866      * @return {Roo.TabPanel}
30867      */
30868     getTabs : function(){
30869         return this.tabs;
30870     },
30871
30872     createTool : function(parentEl, className){
30873         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30874             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30875         btn.addClassOnOver("x-layout-tools-button-over");
30876         return btn;
30877     }
30878 });/*
30879  * Based on:
30880  * Ext JS Library 1.1.1
30881  * Copyright(c) 2006-2007, Ext JS, LLC.
30882  *
30883  * Originally Released Under LGPL - original licence link has changed is not relivant.
30884  *
30885  * Fork - LGPL
30886  * <script type="text/javascript">
30887  */
30888  
30889
30890
30891 /**
30892  * @class Roo.SplitLayoutRegion
30893  * @extends Roo.LayoutRegion
30894  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30895  */
30896 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30897     this.cursor = cursor;
30898     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30899 };
30900
30901 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30902     splitTip : "Drag to resize.",
30903     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30904     useSplitTips : false,
30905
30906     applyConfig : function(config){
30907         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30908         if(config.split){
30909             if(!this.split){
30910                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30911                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30912                 /** The SplitBar for this region 
30913                 * @type Roo.SplitBar */
30914                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30915                 this.split.on("moved", this.onSplitMove, this);
30916                 this.split.useShim = config.useShim === true;
30917                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30918                 if(this.useSplitTips){
30919                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30920                 }
30921                 if(config.collapsible){
30922                     this.split.el.on("dblclick", this.collapse,  this);
30923                 }
30924             }
30925             if(typeof config.minSize != "undefined"){
30926                 this.split.minSize = config.minSize;
30927             }
30928             if(typeof config.maxSize != "undefined"){
30929                 this.split.maxSize = config.maxSize;
30930             }
30931             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30932                 this.hideSplitter();
30933             }
30934         }
30935     },
30936
30937     getHMaxSize : function(){
30938          var cmax = this.config.maxSize || 10000;
30939          var center = this.mgr.getRegion("center");
30940          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30941     },
30942
30943     getVMaxSize : function(){
30944          var cmax = this.config.maxSize || 10000;
30945          var center = this.mgr.getRegion("center");
30946          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30947     },
30948
30949     onSplitMove : function(split, newSize){
30950         this.fireEvent("resized", this, newSize);
30951     },
30952     
30953     /** 
30954      * Returns the {@link Roo.SplitBar} for this region.
30955      * @return {Roo.SplitBar}
30956      */
30957     getSplitBar : function(){
30958         return this.split;
30959     },
30960     
30961     hide : function(){
30962         this.hideSplitter();
30963         Roo.SplitLayoutRegion.superclass.hide.call(this);
30964     },
30965
30966     hideSplitter : function(){
30967         if(this.split){
30968             this.split.el.setLocation(-2000,-2000);
30969             this.split.el.hide();
30970         }
30971     },
30972
30973     show : function(){
30974         if(this.split){
30975             this.split.el.show();
30976         }
30977         Roo.SplitLayoutRegion.superclass.show.call(this);
30978     },
30979     
30980     beforeSlide: function(){
30981         if(Roo.isGecko){// firefox overflow auto bug workaround
30982             this.bodyEl.clip();
30983             if(this.tabs) {
30984                 this.tabs.bodyEl.clip();
30985             }
30986             if(this.activePanel){
30987                 this.activePanel.getEl().clip();
30988                 
30989                 if(this.activePanel.beforeSlide){
30990                     this.activePanel.beforeSlide();
30991                 }
30992             }
30993         }
30994     },
30995     
30996     afterSlide : function(){
30997         if(Roo.isGecko){// firefox overflow auto bug workaround
30998             this.bodyEl.unclip();
30999             if(this.tabs) {
31000                 this.tabs.bodyEl.unclip();
31001             }
31002             if(this.activePanel){
31003                 this.activePanel.getEl().unclip();
31004                 if(this.activePanel.afterSlide){
31005                     this.activePanel.afterSlide();
31006                 }
31007             }
31008         }
31009     },
31010
31011     initAutoHide : function(){
31012         if(this.autoHide !== false){
31013             if(!this.autoHideHd){
31014                 var st = new Roo.util.DelayedTask(this.slideIn, this);
31015                 this.autoHideHd = {
31016                     "mouseout": function(e){
31017                         if(!e.within(this.el, true)){
31018                             st.delay(500);
31019                         }
31020                     },
31021                     "mouseover" : function(e){
31022                         st.cancel();
31023                     },
31024                     scope : this
31025                 };
31026             }
31027             this.el.on(this.autoHideHd);
31028         }
31029     },
31030
31031     clearAutoHide : function(){
31032         if(this.autoHide !== false){
31033             this.el.un("mouseout", this.autoHideHd.mouseout);
31034             this.el.un("mouseover", this.autoHideHd.mouseover);
31035         }
31036     },
31037
31038     clearMonitor : function(){
31039         Roo.get(document).un("click", this.slideInIf, this);
31040     },
31041
31042     // these names are backwards but not changed for compat
31043     slideOut : function(){
31044         if(this.isSlid || this.el.hasActiveFx()){
31045             return;
31046         }
31047         this.isSlid = true;
31048         if(this.collapseBtn){
31049             this.collapseBtn.hide();
31050         }
31051         this.closeBtnState = this.closeBtn.getStyle('display');
31052         this.closeBtn.hide();
31053         if(this.stickBtn){
31054             this.stickBtn.show();
31055         }
31056         this.el.show();
31057         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
31058         this.beforeSlide();
31059         this.el.setStyle("z-index", 10001);
31060         this.el.slideIn(this.getSlideAnchor(), {
31061             callback: function(){
31062                 this.afterSlide();
31063                 this.initAutoHide();
31064                 Roo.get(document).on("click", this.slideInIf, this);
31065                 this.fireEvent("slideshow", this);
31066             },
31067             scope: this,
31068             block: true
31069         });
31070     },
31071
31072     afterSlideIn : function(){
31073         this.clearAutoHide();
31074         this.isSlid = false;
31075         this.clearMonitor();
31076         this.el.setStyle("z-index", "");
31077         if(this.collapseBtn){
31078             this.collapseBtn.show();
31079         }
31080         this.closeBtn.setStyle('display', this.closeBtnState);
31081         if(this.stickBtn){
31082             this.stickBtn.hide();
31083         }
31084         this.fireEvent("slidehide", this);
31085     },
31086
31087     slideIn : function(cb){
31088         if(!this.isSlid || this.el.hasActiveFx()){
31089             Roo.callback(cb);
31090             return;
31091         }
31092         this.isSlid = false;
31093         this.beforeSlide();
31094         this.el.slideOut(this.getSlideAnchor(), {
31095             callback: function(){
31096                 this.el.setLeftTop(-10000, -10000);
31097                 this.afterSlide();
31098                 this.afterSlideIn();
31099                 Roo.callback(cb);
31100             },
31101             scope: this,
31102             block: true
31103         });
31104     },
31105     
31106     slideInIf : function(e){
31107         if(!e.within(this.el)){
31108             this.slideIn();
31109         }
31110     },
31111
31112     animateCollapse : function(){
31113         this.beforeSlide();
31114         this.el.setStyle("z-index", 20000);
31115         var anchor = this.getSlideAnchor();
31116         this.el.slideOut(anchor, {
31117             callback : function(){
31118                 this.el.setStyle("z-index", "");
31119                 this.collapsedEl.slideIn(anchor, {duration:.3});
31120                 this.afterSlide();
31121                 this.el.setLocation(-10000,-10000);
31122                 this.el.hide();
31123                 this.fireEvent("collapsed", this);
31124             },
31125             scope: this,
31126             block: true
31127         });
31128     },
31129
31130     animateExpand : function(){
31131         this.beforeSlide();
31132         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
31133         this.el.setStyle("z-index", 20000);
31134         this.collapsedEl.hide({
31135             duration:.1
31136         });
31137         this.el.slideIn(this.getSlideAnchor(), {
31138             callback : function(){
31139                 this.el.setStyle("z-index", "");
31140                 this.afterSlide();
31141                 if(this.split){
31142                     this.split.el.show();
31143                 }
31144                 this.fireEvent("invalidated", this);
31145                 this.fireEvent("expanded", this);
31146             },
31147             scope: this,
31148             block: true
31149         });
31150     },
31151
31152     anchors : {
31153         "west" : "left",
31154         "east" : "right",
31155         "north" : "top",
31156         "south" : "bottom"
31157     },
31158
31159     sanchors : {
31160         "west" : "l",
31161         "east" : "r",
31162         "north" : "t",
31163         "south" : "b"
31164     },
31165
31166     canchors : {
31167         "west" : "tl-tr",
31168         "east" : "tr-tl",
31169         "north" : "tl-bl",
31170         "south" : "bl-tl"
31171     },
31172
31173     getAnchor : function(){
31174         return this.anchors[this.position];
31175     },
31176
31177     getCollapseAnchor : function(){
31178         return this.canchors[this.position];
31179     },
31180
31181     getSlideAnchor : function(){
31182         return this.sanchors[this.position];
31183     },
31184
31185     getAlignAdj : function(){
31186         var cm = this.cmargins;
31187         switch(this.position){
31188             case "west":
31189                 return [0, 0];
31190             break;
31191             case "east":
31192                 return [0, 0];
31193             break;
31194             case "north":
31195                 return [0, 0];
31196             break;
31197             case "south":
31198                 return [0, 0];
31199             break;
31200         }
31201     },
31202
31203     getExpandAdj : function(){
31204         var c = this.collapsedEl, cm = this.cmargins;
31205         switch(this.position){
31206             case "west":
31207                 return [-(cm.right+c.getWidth()+cm.left), 0];
31208             break;
31209             case "east":
31210                 return [cm.right+c.getWidth()+cm.left, 0];
31211             break;
31212             case "north":
31213                 return [0, -(cm.top+cm.bottom+c.getHeight())];
31214             break;
31215             case "south":
31216                 return [0, cm.top+cm.bottom+c.getHeight()];
31217             break;
31218         }
31219     }
31220 });/*
31221  * Based on:
31222  * Ext JS Library 1.1.1
31223  * Copyright(c) 2006-2007, Ext JS, LLC.
31224  *
31225  * Originally Released Under LGPL - original licence link has changed is not relivant.
31226  *
31227  * Fork - LGPL
31228  * <script type="text/javascript">
31229  */
31230 /*
31231  * These classes are private internal classes
31232  */
31233 Roo.CenterLayoutRegion = function(mgr, config){
31234     Roo.LayoutRegion.call(this, mgr, config, "center");
31235     this.visible = true;
31236     this.minWidth = config.minWidth || 20;
31237     this.minHeight = config.minHeight || 20;
31238 };
31239
31240 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
31241     hide : function(){
31242         // center panel can't be hidden
31243     },
31244     
31245     show : function(){
31246         // center panel can't be hidden
31247     },
31248     
31249     getMinWidth: function(){
31250         return this.minWidth;
31251     },
31252     
31253     getMinHeight: function(){
31254         return this.minHeight;
31255     }
31256 });
31257
31258
31259 Roo.NorthLayoutRegion = function(mgr, config){
31260     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
31261     if(this.split){
31262         this.split.placement = Roo.SplitBar.TOP;
31263         this.split.orientation = Roo.SplitBar.VERTICAL;
31264         this.split.el.addClass("x-layout-split-v");
31265     }
31266     var size = config.initialSize || config.height;
31267     if(typeof size != "undefined"){
31268         this.el.setHeight(size);
31269     }
31270 };
31271 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
31272     orientation: Roo.SplitBar.VERTICAL,
31273     getBox : function(){
31274         if(this.collapsed){
31275             return this.collapsedEl.getBox();
31276         }
31277         var box = this.el.getBox();
31278         if(this.split){
31279             box.height += this.split.el.getHeight();
31280         }
31281         return box;
31282     },
31283     
31284     updateBox : function(box){
31285         if(this.split && !this.collapsed){
31286             box.height -= this.split.el.getHeight();
31287             this.split.el.setLeft(box.x);
31288             this.split.el.setTop(box.y+box.height);
31289             this.split.el.setWidth(box.width);
31290         }
31291         if(this.collapsed){
31292             this.updateBody(box.width, null);
31293         }
31294         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31295     }
31296 });
31297
31298 Roo.SouthLayoutRegion = function(mgr, config){
31299     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
31300     if(this.split){
31301         this.split.placement = Roo.SplitBar.BOTTOM;
31302         this.split.orientation = Roo.SplitBar.VERTICAL;
31303         this.split.el.addClass("x-layout-split-v");
31304     }
31305     var size = config.initialSize || config.height;
31306     if(typeof size != "undefined"){
31307         this.el.setHeight(size);
31308     }
31309 };
31310 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
31311     orientation: Roo.SplitBar.VERTICAL,
31312     getBox : function(){
31313         if(this.collapsed){
31314             return this.collapsedEl.getBox();
31315         }
31316         var box = this.el.getBox();
31317         if(this.split){
31318             var sh = this.split.el.getHeight();
31319             box.height += sh;
31320             box.y -= sh;
31321         }
31322         return box;
31323     },
31324     
31325     updateBox : function(box){
31326         if(this.split && !this.collapsed){
31327             var sh = this.split.el.getHeight();
31328             box.height -= sh;
31329             box.y += sh;
31330             this.split.el.setLeft(box.x);
31331             this.split.el.setTop(box.y-sh);
31332             this.split.el.setWidth(box.width);
31333         }
31334         if(this.collapsed){
31335             this.updateBody(box.width, null);
31336         }
31337         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31338     }
31339 });
31340
31341 Roo.EastLayoutRegion = function(mgr, config){
31342     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31343     if(this.split){
31344         this.split.placement = Roo.SplitBar.RIGHT;
31345         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31346         this.split.el.addClass("x-layout-split-h");
31347     }
31348     var size = config.initialSize || config.width;
31349     if(typeof size != "undefined"){
31350         this.el.setWidth(size);
31351     }
31352 };
31353 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31354     orientation: Roo.SplitBar.HORIZONTAL,
31355     getBox : function(){
31356         if(this.collapsed){
31357             return this.collapsedEl.getBox();
31358         }
31359         var box = this.el.getBox();
31360         if(this.split){
31361             var sw = this.split.el.getWidth();
31362             box.width += sw;
31363             box.x -= sw;
31364         }
31365         return box;
31366     },
31367
31368     updateBox : function(box){
31369         if(this.split && !this.collapsed){
31370             var sw = this.split.el.getWidth();
31371             box.width -= sw;
31372             this.split.el.setLeft(box.x);
31373             this.split.el.setTop(box.y);
31374             this.split.el.setHeight(box.height);
31375             box.x += sw;
31376         }
31377         if(this.collapsed){
31378             this.updateBody(null, box.height);
31379         }
31380         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31381     }
31382 });
31383
31384 Roo.WestLayoutRegion = function(mgr, config){
31385     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31386     if(this.split){
31387         this.split.placement = Roo.SplitBar.LEFT;
31388         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31389         this.split.el.addClass("x-layout-split-h");
31390     }
31391     var size = config.initialSize || config.width;
31392     if(typeof size != "undefined"){
31393         this.el.setWidth(size);
31394     }
31395 };
31396 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31397     orientation: Roo.SplitBar.HORIZONTAL,
31398     getBox : function(){
31399         if(this.collapsed){
31400             return this.collapsedEl.getBox();
31401         }
31402         var box = this.el.getBox();
31403         if(this.split){
31404             box.width += this.split.el.getWidth();
31405         }
31406         return box;
31407     },
31408     
31409     updateBox : function(box){
31410         if(this.split && !this.collapsed){
31411             var sw = this.split.el.getWidth();
31412             box.width -= sw;
31413             this.split.el.setLeft(box.x+box.width);
31414             this.split.el.setTop(box.y);
31415             this.split.el.setHeight(box.height);
31416         }
31417         if(this.collapsed){
31418             this.updateBody(null, box.height);
31419         }
31420         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31421     }
31422 });
31423 /*
31424  * Based on:
31425  * Ext JS Library 1.1.1
31426  * Copyright(c) 2006-2007, Ext JS, LLC.
31427  *
31428  * Originally Released Under LGPL - original licence link has changed is not relivant.
31429  *
31430  * Fork - LGPL
31431  * <script type="text/javascript">
31432  */
31433  
31434  
31435 /*
31436  * Private internal class for reading and applying state
31437  */
31438 Roo.LayoutStateManager = function(layout){
31439      // default empty state
31440      this.state = {
31441         north: {},
31442         south: {},
31443         east: {},
31444         west: {}       
31445     };
31446 };
31447
31448 Roo.LayoutStateManager.prototype = {
31449     init : function(layout, provider){
31450         this.provider = provider;
31451         var state = provider.get(layout.id+"-layout-state");
31452         if(state){
31453             var wasUpdating = layout.isUpdating();
31454             if(!wasUpdating){
31455                 layout.beginUpdate();
31456             }
31457             for(var key in state){
31458                 if(typeof state[key] != "function"){
31459                     var rstate = state[key];
31460                     var r = layout.getRegion(key);
31461                     if(r && rstate){
31462                         if(rstate.size){
31463                             r.resizeTo(rstate.size);
31464                         }
31465                         if(rstate.collapsed == true){
31466                             r.collapse(true);
31467                         }else{
31468                             r.expand(null, true);
31469                         }
31470                     }
31471                 }
31472             }
31473             if(!wasUpdating){
31474                 layout.endUpdate();
31475             }
31476             this.state = state; 
31477         }
31478         this.layout = layout;
31479         layout.on("regionresized", this.onRegionResized, this);
31480         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31481         layout.on("regionexpanded", this.onRegionExpanded, this);
31482     },
31483     
31484     storeState : function(){
31485         this.provider.set(this.layout.id+"-layout-state", this.state);
31486     },
31487     
31488     onRegionResized : function(region, newSize){
31489         this.state[region.getPosition()].size = newSize;
31490         this.storeState();
31491     },
31492     
31493     onRegionCollapsed : function(region){
31494         this.state[region.getPosition()].collapsed = true;
31495         this.storeState();
31496     },
31497     
31498     onRegionExpanded : function(region){
31499         this.state[region.getPosition()].collapsed = false;
31500         this.storeState();
31501     }
31502 };/*
31503  * Based on:
31504  * Ext JS Library 1.1.1
31505  * Copyright(c) 2006-2007, Ext JS, LLC.
31506  *
31507  * Originally Released Under LGPL - original licence link has changed is not relivant.
31508  *
31509  * Fork - LGPL
31510  * <script type="text/javascript">
31511  */
31512 /**
31513  * @class Roo.ContentPanel
31514  * @extends Roo.util.Observable
31515  * A basic ContentPanel element.
31516  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31517  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31518  * @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
31519  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31520  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31521  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31522  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31523  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31524  * @cfg {String} title          The title for this panel
31525  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31526  * @cfg {String} url            Calls {@link #setUrl} with this value
31527  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31528  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31529  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31530  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31531
31532  * @constructor
31533  * Create a new ContentPanel.
31534  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31535  * @param {String/Object} config A string to set only the title or a config object
31536  * @param {String} content (optional) Set the HTML content for this panel
31537  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31538  */
31539 Roo.ContentPanel = function(el, config, content){
31540     
31541      
31542     /*
31543     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31544         config = el;
31545         el = Roo.id();
31546     }
31547     if (config && config.parentLayout) { 
31548         el = config.parentLayout.el.createChild(); 
31549     }
31550     */
31551     if(el.autoCreate){ // xtype is available if this is called from factory
31552         config = el;
31553         el = Roo.id();
31554     }
31555     this.el = Roo.get(el);
31556     if(!this.el && config && config.autoCreate){
31557         if(typeof config.autoCreate == "object"){
31558             if(!config.autoCreate.id){
31559                 config.autoCreate.id = config.id||el;
31560             }
31561             this.el = Roo.DomHelper.append(document.body,
31562                         config.autoCreate, true);
31563         }else{
31564             this.el = Roo.DomHelper.append(document.body,
31565                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31566         }
31567     }
31568     this.closable = false;
31569     this.loaded = false;
31570     this.active = false;
31571     if(typeof config == "string"){
31572         this.title = config;
31573     }else{
31574         Roo.apply(this, config);
31575     }
31576     
31577     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31578         this.wrapEl = this.el.wrap();
31579         this.toolbar.container = this.el.insertSibling(false, 'before');
31580         this.toolbar = new Roo.Toolbar(this.toolbar);
31581     }
31582     
31583     // xtype created footer. - not sure if will work as we normally have to render first..
31584     if (this.footer && !this.footer.el && this.footer.xtype) {
31585         if (!this.wrapEl) {
31586             this.wrapEl = this.el.wrap();
31587         }
31588     
31589         this.footer.container = this.wrapEl.createChild();
31590          
31591         this.footer = Roo.factory(this.footer, Roo);
31592         
31593     }
31594     
31595     if(this.resizeEl){
31596         this.resizeEl = Roo.get(this.resizeEl, true);
31597     }else{
31598         this.resizeEl = this.el;
31599     }
31600     // handle view.xtype
31601     
31602  
31603     
31604     
31605     this.addEvents({
31606         /**
31607          * @event activate
31608          * Fires when this panel is activated. 
31609          * @param {Roo.ContentPanel} this
31610          */
31611         "activate" : true,
31612         /**
31613          * @event deactivate
31614          * Fires when this panel is activated. 
31615          * @param {Roo.ContentPanel} this
31616          */
31617         "deactivate" : true,
31618
31619         /**
31620          * @event resize
31621          * Fires when this panel is resized if fitToFrame is true.
31622          * @param {Roo.ContentPanel} this
31623          * @param {Number} width The width after any component adjustments
31624          * @param {Number} height The height after any component adjustments
31625          */
31626         "resize" : true,
31627         
31628          /**
31629          * @event render
31630          * Fires when this tab is created
31631          * @param {Roo.ContentPanel} this
31632          */
31633         "render" : true
31634          
31635         
31636     });
31637     
31638
31639     
31640     
31641     if(this.autoScroll){
31642         this.resizeEl.setStyle("overflow", "auto");
31643     } else {
31644         // fix randome scrolling
31645         this.el.on('scroll', function() {
31646             Roo.log('fix random scolling');
31647             this.scrollTo('top',0); 
31648         });
31649     }
31650     content = content || this.content;
31651     if(content){
31652         this.setContent(content);
31653     }
31654     if(config && config.url){
31655         this.setUrl(this.url, this.params, this.loadOnce);
31656     }
31657     
31658     
31659     
31660     Roo.ContentPanel.superclass.constructor.call(this);
31661     
31662     if (this.view && typeof(this.view.xtype) != 'undefined') {
31663         this.view.el = this.el.appendChild(document.createElement("div"));
31664         this.view = Roo.factory(this.view); 
31665         this.view.render  &&  this.view.render(false, '');  
31666     }
31667     
31668     
31669     this.fireEvent('render', this);
31670 };
31671
31672 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31673     tabTip:'',
31674     setRegion : function(region){
31675         this.region = region;
31676         if(region){
31677            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31678         }else{
31679            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31680         } 
31681     },
31682     
31683     /**
31684      * Returns the toolbar for this Panel if one was configured. 
31685      * @return {Roo.Toolbar} 
31686      */
31687     getToolbar : function(){
31688         return this.toolbar;
31689     },
31690     
31691     setActiveState : function(active){
31692         this.active = active;
31693         if(!active){
31694             this.fireEvent("deactivate", this);
31695         }else{
31696             this.fireEvent("activate", this);
31697         }
31698     },
31699     /**
31700      * Updates this panel's element
31701      * @param {String} content The new content
31702      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31703     */
31704     setContent : function(content, loadScripts){
31705         this.el.update(content, loadScripts);
31706     },
31707
31708     ignoreResize : function(w, h){
31709         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31710             return true;
31711         }else{
31712             this.lastSize = {width: w, height: h};
31713             return false;
31714         }
31715     },
31716     /**
31717      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31718      * @return {Roo.UpdateManager} The UpdateManager
31719      */
31720     getUpdateManager : function(){
31721         return this.el.getUpdateManager();
31722     },
31723      /**
31724      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31725      * @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:
31726 <pre><code>
31727 panel.load({
31728     url: "your-url.php",
31729     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31730     callback: yourFunction,
31731     scope: yourObject, //(optional scope)
31732     discardUrl: false,
31733     nocache: false,
31734     text: "Loading...",
31735     timeout: 30,
31736     scripts: false
31737 });
31738 </code></pre>
31739      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31740      * 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.
31741      * @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}
31742      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31743      * @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.
31744      * @return {Roo.ContentPanel} this
31745      */
31746     load : function(){
31747         var um = this.el.getUpdateManager();
31748         um.update.apply(um, arguments);
31749         return this;
31750     },
31751
31752
31753     /**
31754      * 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.
31755      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31756      * @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)
31757      * @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)
31758      * @return {Roo.UpdateManager} The UpdateManager
31759      */
31760     setUrl : function(url, params, loadOnce){
31761         if(this.refreshDelegate){
31762             this.removeListener("activate", this.refreshDelegate);
31763         }
31764         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31765         this.on("activate", this.refreshDelegate);
31766         return this.el.getUpdateManager();
31767     },
31768     
31769     _handleRefresh : function(url, params, loadOnce){
31770         if(!loadOnce || !this.loaded){
31771             var updater = this.el.getUpdateManager();
31772             updater.update(url, params, this._setLoaded.createDelegate(this));
31773         }
31774     },
31775     
31776     _setLoaded : function(){
31777         this.loaded = true;
31778     }, 
31779     
31780     /**
31781      * Returns this panel's id
31782      * @return {String} 
31783      */
31784     getId : function(){
31785         return this.el.id;
31786     },
31787     
31788     /** 
31789      * Returns this panel's element - used by regiosn to add.
31790      * @return {Roo.Element} 
31791      */
31792     getEl : function(){
31793         return this.wrapEl || this.el;
31794     },
31795     
31796     adjustForComponents : function(width, height)
31797     {
31798         //Roo.log('adjustForComponents ');
31799         if(this.resizeEl != this.el){
31800             width -= this.el.getFrameWidth('lr');
31801             height -= this.el.getFrameWidth('tb');
31802         }
31803         if(this.toolbar){
31804             var te = this.toolbar.getEl();
31805             height -= te.getHeight();
31806             te.setWidth(width);
31807         }
31808         if(this.footer){
31809             var te = this.footer.getEl();
31810             //Roo.log("footer:" + te.getHeight());
31811             
31812             height -= te.getHeight();
31813             te.setWidth(width);
31814         }
31815         
31816         
31817         if(this.adjustments){
31818             width += this.adjustments[0];
31819             height += this.adjustments[1];
31820         }
31821         return {"width": width, "height": height};
31822     },
31823     
31824     setSize : function(width, height){
31825         if(this.fitToFrame && !this.ignoreResize(width, height)){
31826             if(this.fitContainer && this.resizeEl != this.el){
31827                 this.el.setSize(width, height);
31828             }
31829             var size = this.adjustForComponents(width, height);
31830             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31831             this.fireEvent('resize', this, size.width, size.height);
31832         }
31833     },
31834     
31835     /**
31836      * Returns this panel's title
31837      * @return {String} 
31838      */
31839     getTitle : function(){
31840         return this.title;
31841     },
31842     
31843     /**
31844      * Set this panel's title
31845      * @param {String} title
31846      */
31847     setTitle : function(title){
31848         this.title = title;
31849         if(this.region){
31850             this.region.updatePanelTitle(this, title);
31851         }
31852     },
31853     
31854     /**
31855      * Returns true is this panel was configured to be closable
31856      * @return {Boolean} 
31857      */
31858     isClosable : function(){
31859         return this.closable;
31860     },
31861     
31862     beforeSlide : function(){
31863         this.el.clip();
31864         this.resizeEl.clip();
31865     },
31866     
31867     afterSlide : function(){
31868         this.el.unclip();
31869         this.resizeEl.unclip();
31870     },
31871     
31872     /**
31873      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31874      *   Will fail silently if the {@link #setUrl} method has not been called.
31875      *   This does not activate the panel, just updates its content.
31876      */
31877     refresh : function(){
31878         if(this.refreshDelegate){
31879            this.loaded = false;
31880            this.refreshDelegate();
31881         }
31882     },
31883     
31884     /**
31885      * Destroys this panel
31886      */
31887     destroy : function(){
31888         this.el.removeAllListeners();
31889         var tempEl = document.createElement("span");
31890         tempEl.appendChild(this.el.dom);
31891         tempEl.innerHTML = "";
31892         this.el.remove();
31893         this.el = null;
31894     },
31895     
31896     /**
31897      * form - if the content panel contains a form - this is a reference to it.
31898      * @type {Roo.form.Form}
31899      */
31900     form : false,
31901     /**
31902      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31903      *    This contains a reference to it.
31904      * @type {Roo.View}
31905      */
31906     view : false,
31907     
31908       /**
31909      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31910      * <pre><code>
31911
31912 layout.addxtype({
31913        xtype : 'Form',
31914        items: [ .... ]
31915    }
31916 );
31917
31918 </code></pre>
31919      * @param {Object} cfg Xtype definition of item to add.
31920      */
31921     
31922     addxtype : function(cfg) {
31923         // add form..
31924         if (cfg.xtype.match(/^Form$/)) {
31925             
31926             var el;
31927             //if (this.footer) {
31928             //    el = this.footer.container.insertSibling(false, 'before');
31929             //} else {
31930                 el = this.el.createChild();
31931             //}
31932
31933             this.form = new  Roo.form.Form(cfg);
31934             
31935             
31936             if ( this.form.allItems.length) {
31937                 this.form.render(el.dom);
31938             }
31939             return this.form;
31940         }
31941         // should only have one of theses..
31942         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31943             // views.. should not be just added - used named prop 'view''
31944             
31945             cfg.el = this.el.appendChild(document.createElement("div"));
31946             // factory?
31947             
31948             var ret = new Roo.factory(cfg);
31949              
31950              ret.render && ret.render(false, ''); // render blank..
31951             this.view = ret;
31952             return ret;
31953         }
31954         return false;
31955     }
31956 });
31957
31958 /**
31959  * @class Roo.GridPanel
31960  * @extends Roo.ContentPanel
31961  * @constructor
31962  * Create a new GridPanel.
31963  * @param {Roo.grid.Grid} grid The grid for this panel
31964  * @param {String/Object} config A string to set only the panel's title, or a config object
31965  */
31966 Roo.GridPanel = function(grid, config){
31967     
31968   
31969     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31970         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31971         
31972     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31973     
31974     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31975     
31976     if(this.toolbar){
31977         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31978     }
31979     // xtype created footer. - not sure if will work as we normally have to render first..
31980     if (this.footer && !this.footer.el && this.footer.xtype) {
31981         
31982         this.footer.container = this.grid.getView().getFooterPanel(true);
31983         this.footer.dataSource = this.grid.dataSource;
31984         this.footer = Roo.factory(this.footer, Roo);
31985         
31986     }
31987     
31988     grid.monitorWindowResize = false; // turn off autosizing
31989     grid.autoHeight = false;
31990     grid.autoWidth = false;
31991     this.grid = grid;
31992     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31993 };
31994
31995 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31996     getId : function(){
31997         return this.grid.id;
31998     },
31999     
32000     /**
32001      * Returns the grid for this panel
32002      * @return {Roo.grid.Grid} 
32003      */
32004     getGrid : function(){
32005         return this.grid;    
32006     },
32007     
32008     setSize : function(width, height){
32009         if(!this.ignoreResize(width, height)){
32010             var grid = this.grid;
32011             var size = this.adjustForComponents(width, height);
32012             grid.getGridEl().setSize(size.width, size.height);
32013             grid.autoSize();
32014         }
32015     },
32016     
32017     beforeSlide : function(){
32018         this.grid.getView().scroller.clip();
32019     },
32020     
32021     afterSlide : function(){
32022         this.grid.getView().scroller.unclip();
32023     },
32024     
32025     destroy : function(){
32026         this.grid.destroy();
32027         delete this.grid;
32028         Roo.GridPanel.superclass.destroy.call(this); 
32029     }
32030 });
32031
32032
32033 /**
32034  * @class Roo.NestedLayoutPanel
32035  * @extends Roo.ContentPanel
32036  * @constructor
32037  * Create a new NestedLayoutPanel.
32038  * 
32039  * 
32040  * @param {Roo.BorderLayout} layout The layout for this panel
32041  * @param {String/Object} config A string to set only the title or a config object
32042  */
32043 Roo.NestedLayoutPanel = function(layout, config)
32044 {
32045     // construct with only one argument..
32046     /* FIXME - implement nicer consturctors
32047     if (layout.layout) {
32048         config = layout;
32049         layout = config.layout;
32050         delete config.layout;
32051     }
32052     if (layout.xtype && !layout.getEl) {
32053         // then layout needs constructing..
32054         layout = Roo.factory(layout, Roo);
32055     }
32056     */
32057     
32058     
32059     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
32060     
32061     layout.monitorWindowResize = false; // turn off autosizing
32062     this.layout = layout;
32063     this.layout.getEl().addClass("x-layout-nested-layout");
32064     
32065     
32066     
32067     
32068 };
32069
32070 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
32071
32072     setSize : function(width, height){
32073         if(!this.ignoreResize(width, height)){
32074             var size = this.adjustForComponents(width, height);
32075             var el = this.layout.getEl();
32076             el.setSize(size.width, size.height);
32077             var touch = el.dom.offsetWidth;
32078             this.layout.layout();
32079             // ie requires a double layout on the first pass
32080             if(Roo.isIE && !this.initialized){
32081                 this.initialized = true;
32082                 this.layout.layout();
32083             }
32084         }
32085     },
32086     
32087     // activate all subpanels if not currently active..
32088     
32089     setActiveState : function(active){
32090         this.active = active;
32091         if(!active){
32092             this.fireEvent("deactivate", this);
32093             return;
32094         }
32095         
32096         this.fireEvent("activate", this);
32097         // not sure if this should happen before or after..
32098         if (!this.layout) {
32099             return; // should not happen..
32100         }
32101         var reg = false;
32102         for (var r in this.layout.regions) {
32103             reg = this.layout.getRegion(r);
32104             if (reg.getActivePanel()) {
32105                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
32106                 reg.setActivePanel(reg.getActivePanel());
32107                 continue;
32108             }
32109             if (!reg.panels.length) {
32110                 continue;
32111             }
32112             reg.showPanel(reg.getPanel(0));
32113         }
32114         
32115         
32116         
32117         
32118     },
32119     
32120     /**
32121      * Returns the nested BorderLayout for this panel
32122      * @return {Roo.BorderLayout} 
32123      */
32124     getLayout : function(){
32125         return this.layout;
32126     },
32127     
32128      /**
32129      * Adds a xtype elements to the layout of the nested panel
32130      * <pre><code>
32131
32132 panel.addxtype({
32133        xtype : 'ContentPanel',
32134        region: 'west',
32135        items: [ .... ]
32136    }
32137 );
32138
32139 panel.addxtype({
32140         xtype : 'NestedLayoutPanel',
32141         region: 'west',
32142         layout: {
32143            center: { },
32144            west: { }   
32145         },
32146         items : [ ... list of content panels or nested layout panels.. ]
32147    }
32148 );
32149 </code></pre>
32150      * @param {Object} cfg Xtype definition of item to add.
32151      */
32152     addxtype : function(cfg) {
32153         return this.layout.addxtype(cfg);
32154     
32155     }
32156 });
32157
32158 Roo.ScrollPanel = function(el, config, content){
32159     config = config || {};
32160     config.fitToFrame = true;
32161     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
32162     
32163     this.el.dom.style.overflow = "hidden";
32164     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
32165     this.el.removeClass("x-layout-inactive-content");
32166     this.el.on("mousewheel", this.onWheel, this);
32167
32168     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
32169     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
32170     up.unselectable(); down.unselectable();
32171     up.on("click", this.scrollUp, this);
32172     down.on("click", this.scrollDown, this);
32173     up.addClassOnOver("x-scroller-btn-over");
32174     down.addClassOnOver("x-scroller-btn-over");
32175     up.addClassOnClick("x-scroller-btn-click");
32176     down.addClassOnClick("x-scroller-btn-click");
32177     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
32178
32179     this.resizeEl = this.el;
32180     this.el = wrap; this.up = up; this.down = down;
32181 };
32182
32183 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
32184     increment : 100,
32185     wheelIncrement : 5,
32186     scrollUp : function(){
32187         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
32188     },
32189
32190     scrollDown : function(){
32191         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
32192     },
32193
32194     afterScroll : function(){
32195         var el = this.resizeEl;
32196         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
32197         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32198         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
32199     },
32200
32201     setSize : function(){
32202         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
32203         this.afterScroll();
32204     },
32205
32206     onWheel : function(e){
32207         var d = e.getWheelDelta();
32208         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
32209         this.afterScroll();
32210         e.stopEvent();
32211     },
32212
32213     setContent : function(content, loadScripts){
32214         this.resizeEl.update(content, loadScripts);
32215     }
32216
32217 });
32218
32219
32220
32221
32222
32223
32224
32225
32226
32227 /**
32228  * @class Roo.TreePanel
32229  * @extends Roo.ContentPanel
32230  * @constructor
32231  * Create a new TreePanel. - defaults to fit/scoll contents.
32232  * @param {String/Object} config A string to set only the panel's title, or a config object
32233  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
32234  */
32235 Roo.TreePanel = function(config){
32236     var el = config.el;
32237     var tree = config.tree;
32238     delete config.tree; 
32239     delete config.el; // hopefull!
32240     
32241     // wrapper for IE7 strict & safari scroll issue
32242     
32243     var treeEl = el.createChild();
32244     config.resizeEl = treeEl;
32245     
32246     
32247     
32248     Roo.TreePanel.superclass.constructor.call(this, el, config);
32249  
32250  
32251     this.tree = new Roo.tree.TreePanel(treeEl , tree);
32252     //console.log(tree);
32253     this.on('activate', function()
32254     {
32255         if (this.tree.rendered) {
32256             return;
32257         }
32258         //console.log('render tree');
32259         this.tree.render();
32260     });
32261     // this should not be needed.. - it's actually the 'el' that resizes?
32262     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
32263     
32264     //this.on('resize',  function (cp, w, h) {
32265     //        this.tree.innerCt.setWidth(w);
32266     //        this.tree.innerCt.setHeight(h);
32267     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
32268     //});
32269
32270         
32271     
32272 };
32273
32274 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
32275     fitToFrame : true,
32276     autoScroll : true
32277 });
32278
32279
32280
32281
32282
32283
32284
32285
32286
32287
32288
32289 /*
32290  * Based on:
32291  * Ext JS Library 1.1.1
32292  * Copyright(c) 2006-2007, Ext JS, LLC.
32293  *
32294  * Originally Released Under LGPL - original licence link has changed is not relivant.
32295  *
32296  * Fork - LGPL
32297  * <script type="text/javascript">
32298  */
32299  
32300
32301 /**
32302  * @class Roo.ReaderLayout
32303  * @extends Roo.BorderLayout
32304  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
32305  * center region containing two nested regions (a top one for a list view and one for item preview below),
32306  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
32307  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
32308  * expedites the setup of the overall layout and regions for this common application style.
32309  * Example:
32310  <pre><code>
32311 var reader = new Roo.ReaderLayout();
32312 var CP = Roo.ContentPanel;  // shortcut for adding
32313
32314 reader.beginUpdate();
32315 reader.add("north", new CP("north", "North"));
32316 reader.add("west", new CP("west", {title: "West"}));
32317 reader.add("east", new CP("east", {title: "East"}));
32318
32319 reader.regions.listView.add(new CP("listView", "List"));
32320 reader.regions.preview.add(new CP("preview", "Preview"));
32321 reader.endUpdate();
32322 </code></pre>
32323 * @constructor
32324 * Create a new ReaderLayout
32325 * @param {Object} config Configuration options
32326 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
32327 * document.body if omitted)
32328 */
32329 Roo.ReaderLayout = function(config, renderTo){
32330     var c = config || {size:{}};
32331     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32332         north: c.north !== false ? Roo.apply({
32333             split:false,
32334             initialSize: 32,
32335             titlebar: false
32336         }, c.north) : false,
32337         west: c.west !== false ? Roo.apply({
32338             split:true,
32339             initialSize: 200,
32340             minSize: 175,
32341             maxSize: 400,
32342             titlebar: true,
32343             collapsible: true,
32344             animate: true,
32345             margins:{left:5,right:0,bottom:5,top:5},
32346             cmargins:{left:5,right:5,bottom:5,top:5}
32347         }, c.west) : false,
32348         east: c.east !== false ? Roo.apply({
32349             split:true,
32350             initialSize: 200,
32351             minSize: 175,
32352             maxSize: 400,
32353             titlebar: true,
32354             collapsible: true,
32355             animate: true,
32356             margins:{left:0,right:5,bottom:5,top:5},
32357             cmargins:{left:5,right:5,bottom:5,top:5}
32358         }, c.east) : false,
32359         center: Roo.apply({
32360             tabPosition: 'top',
32361             autoScroll:false,
32362             closeOnTab: true,
32363             titlebar:false,
32364             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32365         }, c.center)
32366     });
32367
32368     this.el.addClass('x-reader');
32369
32370     this.beginUpdate();
32371
32372     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32373         south: c.preview !== false ? Roo.apply({
32374             split:true,
32375             initialSize: 200,
32376             minSize: 100,
32377             autoScroll:true,
32378             collapsible:true,
32379             titlebar: true,
32380             cmargins:{top:5,left:0, right:0, bottom:0}
32381         }, c.preview) : false,
32382         center: Roo.apply({
32383             autoScroll:false,
32384             titlebar:false,
32385             minHeight:200
32386         }, c.listView)
32387     });
32388     this.add('center', new Roo.NestedLayoutPanel(inner,
32389             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32390
32391     this.endUpdate();
32392
32393     this.regions.preview = inner.getRegion('south');
32394     this.regions.listView = inner.getRegion('center');
32395 };
32396
32397 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32398  * Based on:
32399  * Ext JS Library 1.1.1
32400  * Copyright(c) 2006-2007, Ext JS, LLC.
32401  *
32402  * Originally Released Under LGPL - original licence link has changed is not relivant.
32403  *
32404  * Fork - LGPL
32405  * <script type="text/javascript">
32406  */
32407  
32408 /**
32409  * @class Roo.grid.Grid
32410  * @extends Roo.util.Observable
32411  * This class represents the primary interface of a component based grid control.
32412  * <br><br>Usage:<pre><code>
32413  var grid = new Roo.grid.Grid("my-container-id", {
32414      ds: myDataStore,
32415      cm: myColModel,
32416      selModel: mySelectionModel,
32417      autoSizeColumns: true,
32418      monitorWindowResize: false,
32419      trackMouseOver: true
32420  });
32421  // set any options
32422  grid.render();
32423  * </code></pre>
32424  * <b>Common Problems:</b><br/>
32425  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32426  * element will correct this<br/>
32427  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32428  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32429  * are unpredictable.<br/>
32430  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32431  * grid to calculate dimensions/offsets.<br/>
32432   * @constructor
32433  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32434  * The container MUST have some type of size defined for the grid to fill. The container will be
32435  * automatically set to position relative if it isn't already.
32436  * @param {Object} config A config object that sets properties on this grid.
32437  */
32438 Roo.grid.Grid = function(container, config){
32439         // initialize the container
32440         this.container = Roo.get(container);
32441         this.container.update("");
32442         this.container.setStyle("overflow", "hidden");
32443     this.container.addClass('x-grid-container');
32444
32445     this.id = this.container.id;
32446
32447     Roo.apply(this, config);
32448     // check and correct shorthanded configs
32449     if(this.ds){
32450         this.dataSource = this.ds;
32451         delete this.ds;
32452     }
32453     if(this.cm){
32454         this.colModel = this.cm;
32455         delete this.cm;
32456     }
32457     if(this.sm){
32458         this.selModel = this.sm;
32459         delete this.sm;
32460     }
32461
32462     if (this.selModel) {
32463         this.selModel = Roo.factory(this.selModel, Roo.grid);
32464         this.sm = this.selModel;
32465         this.sm.xmodule = this.xmodule || false;
32466     }
32467     if (typeof(this.colModel.config) == 'undefined') {
32468         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32469         this.cm = this.colModel;
32470         this.cm.xmodule = this.xmodule || false;
32471     }
32472     if (this.dataSource) {
32473         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32474         this.ds = this.dataSource;
32475         this.ds.xmodule = this.xmodule || false;
32476          
32477     }
32478     
32479     
32480     
32481     if(this.width){
32482         this.container.setWidth(this.width);
32483     }
32484
32485     if(this.height){
32486         this.container.setHeight(this.height);
32487     }
32488     /** @private */
32489         this.addEvents({
32490         // raw events
32491         /**
32492          * @event click
32493          * The raw click event for the entire grid.
32494          * @param {Roo.EventObject} e
32495          */
32496         "click" : true,
32497         /**
32498          * @event dblclick
32499          * The raw dblclick event for the entire grid.
32500          * @param {Roo.EventObject} e
32501          */
32502         "dblclick" : true,
32503         /**
32504          * @event contextmenu
32505          * The raw contextmenu event for the entire grid.
32506          * @param {Roo.EventObject} e
32507          */
32508         "contextmenu" : true,
32509         /**
32510          * @event mousedown
32511          * The raw mousedown event for the entire grid.
32512          * @param {Roo.EventObject} e
32513          */
32514         "mousedown" : true,
32515         /**
32516          * @event mouseup
32517          * The raw mouseup event for the entire grid.
32518          * @param {Roo.EventObject} e
32519          */
32520         "mouseup" : true,
32521         /**
32522          * @event mouseover
32523          * The raw mouseover event for the entire grid.
32524          * @param {Roo.EventObject} e
32525          */
32526         "mouseover" : true,
32527         /**
32528          * @event mouseout
32529          * The raw mouseout event for the entire grid.
32530          * @param {Roo.EventObject} e
32531          */
32532         "mouseout" : true,
32533         /**
32534          * @event keypress
32535          * The raw keypress event for the entire grid.
32536          * @param {Roo.EventObject} e
32537          */
32538         "keypress" : true,
32539         /**
32540          * @event keydown
32541          * The raw keydown event for the entire grid.
32542          * @param {Roo.EventObject} e
32543          */
32544         "keydown" : true,
32545
32546         // custom events
32547
32548         /**
32549          * @event cellclick
32550          * Fires when a cell is clicked
32551          * @param {Grid} this
32552          * @param {Number} rowIndex
32553          * @param {Number} columnIndex
32554          * @param {Roo.EventObject} e
32555          */
32556         "cellclick" : true,
32557         /**
32558          * @event celldblclick
32559          * Fires when a cell is double clicked
32560          * @param {Grid} this
32561          * @param {Number} rowIndex
32562          * @param {Number} columnIndex
32563          * @param {Roo.EventObject} e
32564          */
32565         "celldblclick" : true,
32566         /**
32567          * @event rowclick
32568          * Fires when a row is clicked
32569          * @param {Grid} this
32570          * @param {Number} rowIndex
32571          * @param {Roo.EventObject} e
32572          */
32573         "rowclick" : true,
32574         /**
32575          * @event rowdblclick
32576          * Fires when a row is double clicked
32577          * @param {Grid} this
32578          * @param {Number} rowIndex
32579          * @param {Roo.EventObject} e
32580          */
32581         "rowdblclick" : true,
32582         /**
32583          * @event headerclick
32584          * Fires when a header is clicked
32585          * @param {Grid} this
32586          * @param {Number} columnIndex
32587          * @param {Roo.EventObject} e
32588          */
32589         "headerclick" : true,
32590         /**
32591          * @event headerdblclick
32592          * Fires when a header cell is double clicked
32593          * @param {Grid} this
32594          * @param {Number} columnIndex
32595          * @param {Roo.EventObject} e
32596          */
32597         "headerdblclick" : true,
32598         /**
32599          * @event rowcontextmenu
32600          * Fires when a row is right clicked
32601          * @param {Grid} this
32602          * @param {Number} rowIndex
32603          * @param {Roo.EventObject} e
32604          */
32605         "rowcontextmenu" : true,
32606         /**
32607          * @event cellcontextmenu
32608          * Fires when a cell is right clicked
32609          * @param {Grid} this
32610          * @param {Number} rowIndex
32611          * @param {Number} cellIndex
32612          * @param {Roo.EventObject} e
32613          */
32614          "cellcontextmenu" : true,
32615         /**
32616          * @event headercontextmenu
32617          * Fires when a header is right clicked
32618          * @param {Grid} this
32619          * @param {Number} columnIndex
32620          * @param {Roo.EventObject} e
32621          */
32622         "headercontextmenu" : true,
32623         /**
32624          * @event bodyscroll
32625          * Fires when the body element is scrolled
32626          * @param {Number} scrollLeft
32627          * @param {Number} scrollTop
32628          */
32629         "bodyscroll" : true,
32630         /**
32631          * @event columnresize
32632          * Fires when the user resizes a column
32633          * @param {Number} columnIndex
32634          * @param {Number} newSize
32635          */
32636         "columnresize" : true,
32637         /**
32638          * @event columnmove
32639          * Fires when the user moves a column
32640          * @param {Number} oldIndex
32641          * @param {Number} newIndex
32642          */
32643         "columnmove" : true,
32644         /**
32645          * @event startdrag
32646          * Fires when row(s) start being dragged
32647          * @param {Grid} this
32648          * @param {Roo.GridDD} dd The drag drop object
32649          * @param {event} e The raw browser event
32650          */
32651         "startdrag" : true,
32652         /**
32653          * @event enddrag
32654          * Fires when a drag operation is complete
32655          * @param {Grid} this
32656          * @param {Roo.GridDD} dd The drag drop object
32657          * @param {event} e The raw browser event
32658          */
32659         "enddrag" : true,
32660         /**
32661          * @event dragdrop
32662          * Fires when dragged row(s) are dropped on a valid DD target
32663          * @param {Grid} this
32664          * @param {Roo.GridDD} dd The drag drop object
32665          * @param {String} targetId The target drag drop object
32666          * @param {event} e The raw browser event
32667          */
32668         "dragdrop" : true,
32669         /**
32670          * @event dragover
32671          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32672          * @param {Grid} this
32673          * @param {Roo.GridDD} dd The drag drop object
32674          * @param {String} targetId The target drag drop object
32675          * @param {event} e The raw browser event
32676          */
32677         "dragover" : true,
32678         /**
32679          * @event dragenter
32680          *  Fires when the dragged row(s) first cross another DD target while being dragged
32681          * @param {Grid} this
32682          * @param {Roo.GridDD} dd The drag drop object
32683          * @param {String} targetId The target drag drop object
32684          * @param {event} e The raw browser event
32685          */
32686         "dragenter" : true,
32687         /**
32688          * @event dragout
32689          * Fires when the dragged row(s) leave another DD target while being dragged
32690          * @param {Grid} this
32691          * @param {Roo.GridDD} dd The drag drop object
32692          * @param {String} targetId The target drag drop object
32693          * @param {event} e The raw browser event
32694          */
32695         "dragout" : true,
32696         /**
32697          * @event rowclass
32698          * Fires when a row is rendered, so you can change add a style to it.
32699          * @param {GridView} gridview   The grid view
32700          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32701          */
32702         'rowclass' : true,
32703
32704         /**
32705          * @event render
32706          * Fires when the grid is rendered
32707          * @param {Grid} grid
32708          */
32709         'render' : true
32710     });
32711
32712     Roo.grid.Grid.superclass.constructor.call(this);
32713 };
32714 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32715     
32716     /**
32717      * @cfg {String} ddGroup - drag drop group.
32718      */
32719
32720     /**
32721      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32722      */
32723     minColumnWidth : 25,
32724
32725     /**
32726      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32727      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32728      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32729      */
32730     autoSizeColumns : false,
32731
32732     /**
32733      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32734      */
32735     autoSizeHeaders : true,
32736
32737     /**
32738      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32739      */
32740     monitorWindowResize : true,
32741
32742     /**
32743      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32744      * rows measured to get a columns size. Default is 0 (all rows).
32745      */
32746     maxRowsToMeasure : 0,
32747
32748     /**
32749      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32750      */
32751     trackMouseOver : true,
32752
32753     /**
32754     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32755     */
32756     
32757     /**
32758     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32759     */
32760     enableDragDrop : false,
32761     
32762     /**
32763     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32764     */
32765     enableColumnMove : true,
32766     
32767     /**
32768     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32769     */
32770     enableColumnHide : true,
32771     
32772     /**
32773     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32774     */
32775     enableRowHeightSync : false,
32776     
32777     /**
32778     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32779     */
32780     stripeRows : true,
32781     
32782     /**
32783     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32784     */
32785     autoHeight : false,
32786
32787     /**
32788      * @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.
32789      */
32790     autoExpandColumn : false,
32791
32792     /**
32793     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32794     * Default is 50.
32795     */
32796     autoExpandMin : 50,
32797
32798     /**
32799     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32800     */
32801     autoExpandMax : 1000,
32802
32803     /**
32804     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32805     */
32806     view : null,
32807
32808     /**
32809     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32810     */
32811     loadMask : false,
32812     /**
32813     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32814     */
32815     dropTarget: false,
32816     
32817    
32818     
32819     // private
32820     rendered : false,
32821
32822     /**
32823     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32824     * of a fixed width. Default is false.
32825     */
32826     /**
32827     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32828     */
32829     /**
32830      * Called once after all setup has been completed and the grid is ready to be rendered.
32831      * @return {Roo.grid.Grid} this
32832      */
32833     render : function()
32834     {
32835         var c = this.container;
32836         // try to detect autoHeight/width mode
32837         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32838             this.autoHeight = true;
32839         }
32840         var view = this.getView();
32841         view.init(this);
32842
32843         c.on("click", this.onClick, this);
32844         c.on("dblclick", this.onDblClick, this);
32845         c.on("contextmenu", this.onContextMenu, this);
32846         c.on("keydown", this.onKeyDown, this);
32847         if (Roo.isTouch) {
32848             c.on("touchstart", this.onTouchStart, this);
32849         }
32850
32851         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32852
32853         this.getSelectionModel().init(this);
32854
32855         view.render();
32856
32857         if(this.loadMask){
32858             this.loadMask = new Roo.LoadMask(this.container,
32859                     Roo.apply({store:this.dataSource}, this.loadMask));
32860         }
32861         
32862         
32863         if (this.toolbar && this.toolbar.xtype) {
32864             this.toolbar.container = this.getView().getHeaderPanel(true);
32865             this.toolbar = new Roo.Toolbar(this.toolbar);
32866         }
32867         if (this.footer && this.footer.xtype) {
32868             this.footer.dataSource = this.getDataSource();
32869             this.footer.container = this.getView().getFooterPanel(true);
32870             this.footer = Roo.factory(this.footer, Roo);
32871         }
32872         if (this.dropTarget && this.dropTarget.xtype) {
32873             delete this.dropTarget.xtype;
32874             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32875         }
32876         
32877         
32878         this.rendered = true;
32879         this.fireEvent('render', this);
32880         return this;
32881     },
32882
32883         /**
32884          * Reconfigures the grid to use a different Store and Column Model.
32885          * The View will be bound to the new objects and refreshed.
32886          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32887          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32888          */
32889     reconfigure : function(dataSource, colModel){
32890         if(this.loadMask){
32891             this.loadMask.destroy();
32892             this.loadMask = new Roo.LoadMask(this.container,
32893                     Roo.apply({store:dataSource}, this.loadMask));
32894         }
32895         this.view.bind(dataSource, colModel);
32896         this.dataSource = dataSource;
32897         this.colModel = colModel;
32898         this.view.refresh(true);
32899     },
32900
32901     // private
32902     onKeyDown : function(e){
32903         this.fireEvent("keydown", e);
32904     },
32905
32906     /**
32907      * Destroy this grid.
32908      * @param {Boolean} removeEl True to remove the element
32909      */
32910     destroy : function(removeEl, keepListeners){
32911         if(this.loadMask){
32912             this.loadMask.destroy();
32913         }
32914         var c = this.container;
32915         c.removeAllListeners();
32916         this.view.destroy();
32917         this.colModel.purgeListeners();
32918         if(!keepListeners){
32919             this.purgeListeners();
32920         }
32921         c.update("");
32922         if(removeEl === true){
32923             c.remove();
32924         }
32925     },
32926
32927     // private
32928     processEvent : function(name, e){
32929         // does this fire select???
32930         //Roo.log('grid:processEvent '  + name);
32931         
32932         if (name != 'touchstart' ) {
32933             this.fireEvent(name, e);    
32934         }
32935         
32936         var t = e.getTarget();
32937         var v = this.view;
32938         var header = v.findHeaderIndex(t);
32939         if(header !== false){
32940             var ename = name == 'touchstart' ? 'click' : name;
32941              
32942             this.fireEvent("header" + ename, this, header, e);
32943         }else{
32944             var row = v.findRowIndex(t);
32945             var cell = v.findCellIndex(t);
32946             if (name == 'touchstart') {
32947                 // first touch is always a click.
32948                 // hopefull this happens after selection is updated.?
32949                 name = false;
32950                 
32951                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32952                     var cs = this.selModel.getSelectedCell();
32953                     if (row == cs[0] && cell == cs[1]){
32954                         name = 'dblclick';
32955                     }
32956                 }
32957                 if (typeof(this.selModel.getSelections) != 'undefined') {
32958                     var cs = this.selModel.getSelections();
32959                     var ds = this.dataSource;
32960                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32961                         name = 'dblclick';
32962                     }
32963                 }
32964                 if (!name) {
32965                     return;
32966                 }
32967             }
32968             
32969             
32970             if(row !== false){
32971                 this.fireEvent("row" + name, this, row, e);
32972                 if(cell !== false){
32973                     this.fireEvent("cell" + name, this, row, cell, e);
32974                 }
32975             }
32976         }
32977     },
32978
32979     // private
32980     onClick : function(e){
32981         this.processEvent("click", e);
32982     },
32983    // private
32984     onTouchStart : function(e){
32985         this.processEvent("touchstart", e);
32986     },
32987
32988     // private
32989     onContextMenu : function(e, t){
32990         this.processEvent("contextmenu", e);
32991     },
32992
32993     // private
32994     onDblClick : function(e){
32995         this.processEvent("dblclick", e);
32996     },
32997
32998     // private
32999     walkCells : function(row, col, step, fn, scope){
33000         var cm = this.colModel, clen = cm.getColumnCount();
33001         var ds = this.dataSource, rlen = ds.getCount(), first = true;
33002         if(step < 0){
33003             if(col < 0){
33004                 row--;
33005                 first = false;
33006             }
33007             while(row >= 0){
33008                 if(!first){
33009                     col = clen-1;
33010                 }
33011                 first = false;
33012                 while(col >= 0){
33013                     if(fn.call(scope || this, row, col, cm) === true){
33014                         return [row, col];
33015                     }
33016                     col--;
33017                 }
33018                 row--;
33019             }
33020         } else {
33021             if(col >= clen){
33022                 row++;
33023                 first = false;
33024             }
33025             while(row < rlen){
33026                 if(!first){
33027                     col = 0;
33028                 }
33029                 first = false;
33030                 while(col < clen){
33031                     if(fn.call(scope || this, row, col, cm) === true){
33032                         return [row, col];
33033                     }
33034                     col++;
33035                 }
33036                 row++;
33037             }
33038         }
33039         return null;
33040     },
33041
33042     // private
33043     getSelections : function(){
33044         return this.selModel.getSelections();
33045     },
33046
33047     /**
33048      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
33049      * but if manual update is required this method will initiate it.
33050      */
33051     autoSize : function(){
33052         if(this.rendered){
33053             this.view.layout();
33054             if(this.view.adjustForScroll){
33055                 this.view.adjustForScroll();
33056             }
33057         }
33058     },
33059
33060     /**
33061      * Returns the grid's underlying element.
33062      * @return {Element} The element
33063      */
33064     getGridEl : function(){
33065         return this.container;
33066     },
33067
33068     // private for compatibility, overridden by editor grid
33069     stopEditing : function(){},
33070
33071     /**
33072      * Returns the grid's SelectionModel.
33073      * @return {SelectionModel}
33074      */
33075     getSelectionModel : function(){
33076         if(!this.selModel){
33077             this.selModel = new Roo.grid.RowSelectionModel();
33078         }
33079         return this.selModel;
33080     },
33081
33082     /**
33083      * Returns the grid's DataSource.
33084      * @return {DataSource}
33085      */
33086     getDataSource : function(){
33087         return this.dataSource;
33088     },
33089
33090     /**
33091      * Returns the grid's ColumnModel.
33092      * @return {ColumnModel}
33093      */
33094     getColumnModel : function(){
33095         return this.colModel;
33096     },
33097
33098     /**
33099      * Returns the grid's GridView object.
33100      * @return {GridView}
33101      */
33102     getView : function(){
33103         if(!this.view){
33104             this.view = new Roo.grid.GridView(this.viewConfig);
33105         }
33106         return this.view;
33107     },
33108     /**
33109      * Called to get grid's drag proxy text, by default returns this.ddText.
33110      * @return {String}
33111      */
33112     getDragDropText : function(){
33113         var count = this.selModel.getCount();
33114         return String.format(this.ddText, count, count == 1 ? '' : 's');
33115     }
33116 });
33117 /**
33118  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
33119  * %0 is replaced with the number of selected rows.
33120  * @type String
33121  */
33122 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
33123  * Based on:
33124  * Ext JS Library 1.1.1
33125  * Copyright(c) 2006-2007, Ext JS, LLC.
33126  *
33127  * Originally Released Under LGPL - original licence link has changed is not relivant.
33128  *
33129  * Fork - LGPL
33130  * <script type="text/javascript">
33131  */
33132  
33133 Roo.grid.AbstractGridView = function(){
33134         this.grid = null;
33135         
33136         this.events = {
33137             "beforerowremoved" : true,
33138             "beforerowsinserted" : true,
33139             "beforerefresh" : true,
33140             "rowremoved" : true,
33141             "rowsinserted" : true,
33142             "rowupdated" : true,
33143             "refresh" : true
33144         };
33145     Roo.grid.AbstractGridView.superclass.constructor.call(this);
33146 };
33147
33148 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
33149     rowClass : "x-grid-row",
33150     cellClass : "x-grid-cell",
33151     tdClass : "x-grid-td",
33152     hdClass : "x-grid-hd",
33153     splitClass : "x-grid-hd-split",
33154     
33155     init: function(grid){
33156         this.grid = grid;
33157                 var cid = this.grid.getGridEl().id;
33158         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
33159         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
33160         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
33161         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
33162         },
33163         
33164     getColumnRenderers : function(){
33165         var renderers = [];
33166         var cm = this.grid.colModel;
33167         var colCount = cm.getColumnCount();
33168         for(var i = 0; i < colCount; i++){
33169             renderers[i] = cm.getRenderer(i);
33170         }
33171         return renderers;
33172     },
33173     
33174     getColumnIds : function(){
33175         var ids = [];
33176         var cm = this.grid.colModel;
33177         var colCount = cm.getColumnCount();
33178         for(var i = 0; i < colCount; i++){
33179             ids[i] = cm.getColumnId(i);
33180         }
33181         return ids;
33182     },
33183     
33184     getDataIndexes : function(){
33185         if(!this.indexMap){
33186             this.indexMap = this.buildIndexMap();
33187         }
33188         return this.indexMap.colToData;
33189     },
33190     
33191     getColumnIndexByDataIndex : function(dataIndex){
33192         if(!this.indexMap){
33193             this.indexMap = this.buildIndexMap();
33194         }
33195         return this.indexMap.dataToCol[dataIndex];
33196     },
33197     
33198     /**
33199      * Set a css style for a column dynamically. 
33200      * @param {Number} colIndex The index of the column
33201      * @param {String} name The css property name
33202      * @param {String} value The css value
33203      */
33204     setCSSStyle : function(colIndex, name, value){
33205         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
33206         Roo.util.CSS.updateRule(selector, name, value);
33207     },
33208     
33209     generateRules : function(cm){
33210         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
33211         Roo.util.CSS.removeStyleSheet(rulesId);
33212         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33213             var cid = cm.getColumnId(i);
33214             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
33215                          this.tdSelector, cid, " {\n}\n",
33216                          this.hdSelector, cid, " {\n}\n",
33217                          this.splitSelector, cid, " {\n}\n");
33218         }
33219         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33220     }
33221 });/*
33222  * Based on:
33223  * Ext JS Library 1.1.1
33224  * Copyright(c) 2006-2007, Ext JS, LLC.
33225  *
33226  * Originally Released Under LGPL - original licence link has changed is not relivant.
33227  *
33228  * Fork - LGPL
33229  * <script type="text/javascript">
33230  */
33231
33232 // private
33233 // This is a support class used internally by the Grid components
33234 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
33235     this.grid = grid;
33236     this.view = grid.getView();
33237     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33238     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
33239     if(hd2){
33240         this.setHandleElId(Roo.id(hd));
33241         this.setOuterHandleElId(Roo.id(hd2));
33242     }
33243     this.scroll = false;
33244 };
33245 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
33246     maxDragWidth: 120,
33247     getDragData : function(e){
33248         var t = Roo.lib.Event.getTarget(e);
33249         var h = this.view.findHeaderCell(t);
33250         if(h){
33251             return {ddel: h.firstChild, header:h};
33252         }
33253         return false;
33254     },
33255
33256     onInitDrag : function(e){
33257         this.view.headersDisabled = true;
33258         var clone = this.dragData.ddel.cloneNode(true);
33259         clone.id = Roo.id();
33260         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
33261         this.proxy.update(clone);
33262         return true;
33263     },
33264
33265     afterValidDrop : function(){
33266         var v = this.view;
33267         setTimeout(function(){
33268             v.headersDisabled = false;
33269         }, 50);
33270     },
33271
33272     afterInvalidDrop : function(){
33273         var v = this.view;
33274         setTimeout(function(){
33275             v.headersDisabled = false;
33276         }, 50);
33277     }
33278 });
33279 /*
33280  * Based on:
33281  * Ext JS Library 1.1.1
33282  * Copyright(c) 2006-2007, Ext JS, LLC.
33283  *
33284  * Originally Released Under LGPL - original licence link has changed is not relivant.
33285  *
33286  * Fork - LGPL
33287  * <script type="text/javascript">
33288  */
33289 // private
33290 // This is a support class used internally by the Grid components
33291 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
33292     this.grid = grid;
33293     this.view = grid.getView();
33294     // split the proxies so they don't interfere with mouse events
33295     this.proxyTop = Roo.DomHelper.append(document.body, {
33296         cls:"col-move-top", html:"&#160;"
33297     }, true);
33298     this.proxyBottom = Roo.DomHelper.append(document.body, {
33299         cls:"col-move-bottom", html:"&#160;"
33300     }, true);
33301     this.proxyTop.hide = this.proxyBottom.hide = function(){
33302         this.setLeftTop(-100,-100);
33303         this.setStyle("visibility", "hidden");
33304     };
33305     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33306     // temporarily disabled
33307     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33308     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33309 };
33310 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33311     proxyOffsets : [-4, -9],
33312     fly: Roo.Element.fly,
33313
33314     getTargetFromEvent : function(e){
33315         var t = Roo.lib.Event.getTarget(e);
33316         var cindex = this.view.findCellIndex(t);
33317         if(cindex !== false){
33318             return this.view.getHeaderCell(cindex);
33319         }
33320         return null;
33321     },
33322
33323     nextVisible : function(h){
33324         var v = this.view, cm = this.grid.colModel;
33325         h = h.nextSibling;
33326         while(h){
33327             if(!cm.isHidden(v.getCellIndex(h))){
33328                 return h;
33329             }
33330             h = h.nextSibling;
33331         }
33332         return null;
33333     },
33334
33335     prevVisible : function(h){
33336         var v = this.view, cm = this.grid.colModel;
33337         h = h.prevSibling;
33338         while(h){
33339             if(!cm.isHidden(v.getCellIndex(h))){
33340                 return h;
33341             }
33342             h = h.prevSibling;
33343         }
33344         return null;
33345     },
33346
33347     positionIndicator : function(h, n, e){
33348         var x = Roo.lib.Event.getPageX(e);
33349         var r = Roo.lib.Dom.getRegion(n.firstChild);
33350         var px, pt, py = r.top + this.proxyOffsets[1];
33351         if((r.right - x) <= (r.right-r.left)/2){
33352             px = r.right+this.view.borderWidth;
33353             pt = "after";
33354         }else{
33355             px = r.left;
33356             pt = "before";
33357         }
33358         var oldIndex = this.view.getCellIndex(h);
33359         var newIndex = this.view.getCellIndex(n);
33360
33361         if(this.grid.colModel.isFixed(newIndex)){
33362             return false;
33363         }
33364
33365         var locked = this.grid.colModel.isLocked(newIndex);
33366
33367         if(pt == "after"){
33368             newIndex++;
33369         }
33370         if(oldIndex < newIndex){
33371             newIndex--;
33372         }
33373         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33374             return false;
33375         }
33376         px +=  this.proxyOffsets[0];
33377         this.proxyTop.setLeftTop(px, py);
33378         this.proxyTop.show();
33379         if(!this.bottomOffset){
33380             this.bottomOffset = this.view.mainHd.getHeight();
33381         }
33382         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33383         this.proxyBottom.show();
33384         return pt;
33385     },
33386
33387     onNodeEnter : function(n, dd, e, data){
33388         if(data.header != n){
33389             this.positionIndicator(data.header, n, e);
33390         }
33391     },
33392
33393     onNodeOver : function(n, dd, e, data){
33394         var result = false;
33395         if(data.header != n){
33396             result = this.positionIndicator(data.header, n, e);
33397         }
33398         if(!result){
33399             this.proxyTop.hide();
33400             this.proxyBottom.hide();
33401         }
33402         return result ? this.dropAllowed : this.dropNotAllowed;
33403     },
33404
33405     onNodeOut : function(n, dd, e, data){
33406         this.proxyTop.hide();
33407         this.proxyBottom.hide();
33408     },
33409
33410     onNodeDrop : function(n, dd, e, data){
33411         var h = data.header;
33412         if(h != n){
33413             var cm = this.grid.colModel;
33414             var x = Roo.lib.Event.getPageX(e);
33415             var r = Roo.lib.Dom.getRegion(n.firstChild);
33416             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33417             var oldIndex = this.view.getCellIndex(h);
33418             var newIndex = this.view.getCellIndex(n);
33419             var locked = cm.isLocked(newIndex);
33420             if(pt == "after"){
33421                 newIndex++;
33422             }
33423             if(oldIndex < newIndex){
33424                 newIndex--;
33425             }
33426             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33427                 return false;
33428             }
33429             cm.setLocked(oldIndex, locked, true);
33430             cm.moveColumn(oldIndex, newIndex);
33431             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33432             return true;
33433         }
33434         return false;
33435     }
33436 });
33437 /*
33438  * Based on:
33439  * Ext JS Library 1.1.1
33440  * Copyright(c) 2006-2007, Ext JS, LLC.
33441  *
33442  * Originally Released Under LGPL - original licence link has changed is not relivant.
33443  *
33444  * Fork - LGPL
33445  * <script type="text/javascript">
33446  */
33447   
33448 /**
33449  * @class Roo.grid.GridView
33450  * @extends Roo.util.Observable
33451  *
33452  * @constructor
33453  * @param {Object} config
33454  */
33455 Roo.grid.GridView = function(config){
33456     Roo.grid.GridView.superclass.constructor.call(this);
33457     this.el = null;
33458
33459     Roo.apply(this, config);
33460 };
33461
33462 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33463
33464     unselectable :  'unselectable="on"',
33465     unselectableCls :  'x-unselectable',
33466     
33467     
33468     rowClass : "x-grid-row",
33469
33470     cellClass : "x-grid-col",
33471
33472     tdClass : "x-grid-td",
33473
33474     hdClass : "x-grid-hd",
33475
33476     splitClass : "x-grid-split",
33477
33478     sortClasses : ["sort-asc", "sort-desc"],
33479
33480     enableMoveAnim : false,
33481
33482     hlColor: "C3DAF9",
33483
33484     dh : Roo.DomHelper,
33485
33486     fly : Roo.Element.fly,
33487
33488     css : Roo.util.CSS,
33489
33490     borderWidth: 1,
33491
33492     splitOffset: 3,
33493
33494     scrollIncrement : 22,
33495
33496     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33497
33498     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33499
33500     bind : function(ds, cm){
33501         if(this.ds){
33502             this.ds.un("load", this.onLoad, this);
33503             this.ds.un("datachanged", this.onDataChange, this);
33504             this.ds.un("add", this.onAdd, this);
33505             this.ds.un("remove", this.onRemove, this);
33506             this.ds.un("update", this.onUpdate, this);
33507             this.ds.un("clear", this.onClear, this);
33508         }
33509         if(ds){
33510             ds.on("load", this.onLoad, this);
33511             ds.on("datachanged", this.onDataChange, this);
33512             ds.on("add", this.onAdd, this);
33513             ds.on("remove", this.onRemove, this);
33514             ds.on("update", this.onUpdate, this);
33515             ds.on("clear", this.onClear, this);
33516         }
33517         this.ds = ds;
33518
33519         if(this.cm){
33520             this.cm.un("widthchange", this.onColWidthChange, this);
33521             this.cm.un("headerchange", this.onHeaderChange, this);
33522             this.cm.un("hiddenchange", this.onHiddenChange, this);
33523             this.cm.un("columnmoved", this.onColumnMove, this);
33524             this.cm.un("columnlockchange", this.onColumnLock, this);
33525         }
33526         if(cm){
33527             this.generateRules(cm);
33528             cm.on("widthchange", this.onColWidthChange, this);
33529             cm.on("headerchange", this.onHeaderChange, this);
33530             cm.on("hiddenchange", this.onHiddenChange, this);
33531             cm.on("columnmoved", this.onColumnMove, this);
33532             cm.on("columnlockchange", this.onColumnLock, this);
33533         }
33534         this.cm = cm;
33535     },
33536
33537     init: function(grid){
33538         Roo.grid.GridView.superclass.init.call(this, grid);
33539
33540         this.bind(grid.dataSource, grid.colModel);
33541
33542         grid.on("headerclick", this.handleHeaderClick, this);
33543
33544         if(grid.trackMouseOver){
33545             grid.on("mouseover", this.onRowOver, this);
33546             grid.on("mouseout", this.onRowOut, this);
33547         }
33548         grid.cancelTextSelection = function(){};
33549         this.gridId = grid.id;
33550
33551         var tpls = this.templates || {};
33552
33553         if(!tpls.master){
33554             tpls.master = new Roo.Template(
33555                '<div class="x-grid" hidefocus="true">',
33556                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33557                   '<div class="x-grid-topbar"></div>',
33558                   '<div class="x-grid-scroller"><div></div></div>',
33559                   '<div class="x-grid-locked">',
33560                       '<div class="x-grid-header">{lockedHeader}</div>',
33561                       '<div class="x-grid-body">{lockedBody}</div>',
33562                   "</div>",
33563                   '<div class="x-grid-viewport">',
33564                       '<div class="x-grid-header">{header}</div>',
33565                       '<div class="x-grid-body">{body}</div>',
33566                   "</div>",
33567                   '<div class="x-grid-bottombar"></div>',
33568                  
33569                   '<div class="x-grid-resize-proxy">&#160;</div>',
33570                "</div>"
33571             );
33572             tpls.master.disableformats = true;
33573         }
33574
33575         if(!tpls.header){
33576             tpls.header = new Roo.Template(
33577                '<table border="0" cellspacing="0" cellpadding="0">',
33578                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33579                "</table>{splits}"
33580             );
33581             tpls.header.disableformats = true;
33582         }
33583         tpls.header.compile();
33584
33585         if(!tpls.hcell){
33586             tpls.hcell = new Roo.Template(
33587                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33588                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33589                 "</div></td>"
33590              );
33591              tpls.hcell.disableFormats = true;
33592         }
33593         tpls.hcell.compile();
33594
33595         if(!tpls.hsplit){
33596             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33597                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33598             tpls.hsplit.disableFormats = true;
33599         }
33600         tpls.hsplit.compile();
33601
33602         if(!tpls.body){
33603             tpls.body = new Roo.Template(
33604                '<table border="0" cellspacing="0" cellpadding="0">',
33605                "<tbody>{rows}</tbody>",
33606                "</table>"
33607             );
33608             tpls.body.disableFormats = true;
33609         }
33610         tpls.body.compile();
33611
33612         if(!tpls.row){
33613             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33614             tpls.row.disableFormats = true;
33615         }
33616         tpls.row.compile();
33617
33618         if(!tpls.cell){
33619             tpls.cell = new Roo.Template(
33620                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33621                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33622                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33623                 "</td>"
33624             );
33625             tpls.cell.disableFormats = true;
33626         }
33627         tpls.cell.compile();
33628
33629         this.templates = tpls;
33630     },
33631
33632     // remap these for backwards compat
33633     onColWidthChange : function(){
33634         this.updateColumns.apply(this, arguments);
33635     },
33636     onHeaderChange : function(){
33637         this.updateHeaders.apply(this, arguments);
33638     }, 
33639     onHiddenChange : function(){
33640         this.handleHiddenChange.apply(this, arguments);
33641     },
33642     onColumnMove : function(){
33643         this.handleColumnMove.apply(this, arguments);
33644     },
33645     onColumnLock : function(){
33646         this.handleLockChange.apply(this, arguments);
33647     },
33648
33649     onDataChange : function(){
33650         this.refresh();
33651         this.updateHeaderSortState();
33652     },
33653
33654     onClear : function(){
33655         this.refresh();
33656     },
33657
33658     onUpdate : function(ds, record){
33659         this.refreshRow(record);
33660     },
33661
33662     refreshRow : function(record){
33663         var ds = this.ds, index;
33664         if(typeof record == 'number'){
33665             index = record;
33666             record = ds.getAt(index);
33667         }else{
33668             index = ds.indexOf(record);
33669         }
33670         this.insertRows(ds, index, index, true);
33671         this.onRemove(ds, record, index+1, true);
33672         this.syncRowHeights(index, index);
33673         this.layout();
33674         this.fireEvent("rowupdated", this, index, record);
33675     },
33676
33677     onAdd : function(ds, records, index){
33678         this.insertRows(ds, index, index + (records.length-1));
33679     },
33680
33681     onRemove : function(ds, record, index, isUpdate){
33682         if(isUpdate !== true){
33683             this.fireEvent("beforerowremoved", this, index, record);
33684         }
33685         var bt = this.getBodyTable(), lt = this.getLockedTable();
33686         if(bt.rows[index]){
33687             bt.firstChild.removeChild(bt.rows[index]);
33688         }
33689         if(lt.rows[index]){
33690             lt.firstChild.removeChild(lt.rows[index]);
33691         }
33692         if(isUpdate !== true){
33693             this.stripeRows(index);
33694             this.syncRowHeights(index, index);
33695             this.layout();
33696             this.fireEvent("rowremoved", this, index, record);
33697         }
33698     },
33699
33700     onLoad : function(){
33701         this.scrollToTop();
33702     },
33703
33704     /**
33705      * Scrolls the grid to the top
33706      */
33707     scrollToTop : function(){
33708         if(this.scroller){
33709             this.scroller.dom.scrollTop = 0;
33710             this.syncScroll();
33711         }
33712     },
33713
33714     /**
33715      * Gets a panel in the header of the grid that can be used for toolbars etc.
33716      * After modifying the contents of this panel a call to grid.autoSize() may be
33717      * required to register any changes in size.
33718      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33719      * @return Roo.Element
33720      */
33721     getHeaderPanel : function(doShow){
33722         if(doShow){
33723             this.headerPanel.show();
33724         }
33725         return this.headerPanel;
33726     },
33727
33728     /**
33729      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33730      * After modifying the contents of this panel a call to grid.autoSize() may be
33731      * required to register any changes in size.
33732      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33733      * @return Roo.Element
33734      */
33735     getFooterPanel : function(doShow){
33736         if(doShow){
33737             this.footerPanel.show();
33738         }
33739         return this.footerPanel;
33740     },
33741
33742     initElements : function(){
33743         var E = Roo.Element;
33744         var el = this.grid.getGridEl().dom.firstChild;
33745         var cs = el.childNodes;
33746
33747         this.el = new E(el);
33748         
33749          this.focusEl = new E(el.firstChild);
33750         this.focusEl.swallowEvent("click", true);
33751         
33752         this.headerPanel = new E(cs[1]);
33753         this.headerPanel.enableDisplayMode("block");
33754
33755         this.scroller = new E(cs[2]);
33756         this.scrollSizer = new E(this.scroller.dom.firstChild);
33757
33758         this.lockedWrap = new E(cs[3]);
33759         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33760         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33761
33762         this.mainWrap = new E(cs[4]);
33763         this.mainHd = new E(this.mainWrap.dom.firstChild);
33764         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33765
33766         this.footerPanel = new E(cs[5]);
33767         this.footerPanel.enableDisplayMode("block");
33768
33769         this.resizeProxy = new E(cs[6]);
33770
33771         this.headerSelector = String.format(
33772            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33773            this.lockedHd.id, this.mainHd.id
33774         );
33775
33776         this.splitterSelector = String.format(
33777            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33778            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33779         );
33780     },
33781     idToCssName : function(s)
33782     {
33783         return s.replace(/[^a-z0-9]+/ig, '-');
33784     },
33785
33786     getHeaderCell : function(index){
33787         return Roo.DomQuery.select(this.headerSelector)[index];
33788     },
33789
33790     getHeaderCellMeasure : function(index){
33791         return this.getHeaderCell(index).firstChild;
33792     },
33793
33794     getHeaderCellText : function(index){
33795         return this.getHeaderCell(index).firstChild.firstChild;
33796     },
33797
33798     getLockedTable : function(){
33799         return this.lockedBody.dom.firstChild;
33800     },
33801
33802     getBodyTable : function(){
33803         return this.mainBody.dom.firstChild;
33804     },
33805
33806     getLockedRow : function(index){
33807         return this.getLockedTable().rows[index];
33808     },
33809
33810     getRow : function(index){
33811         return this.getBodyTable().rows[index];
33812     },
33813
33814     getRowComposite : function(index){
33815         if(!this.rowEl){
33816             this.rowEl = new Roo.CompositeElementLite();
33817         }
33818         var els = [], lrow, mrow;
33819         if(lrow = this.getLockedRow(index)){
33820             els.push(lrow);
33821         }
33822         if(mrow = this.getRow(index)){
33823             els.push(mrow);
33824         }
33825         this.rowEl.elements = els;
33826         return this.rowEl;
33827     },
33828     /**
33829      * Gets the 'td' of the cell
33830      * 
33831      * @param {Integer} rowIndex row to select
33832      * @param {Integer} colIndex column to select
33833      * 
33834      * @return {Object} 
33835      */
33836     getCell : function(rowIndex, colIndex){
33837         var locked = this.cm.getLockedCount();
33838         var source;
33839         if(colIndex < locked){
33840             source = this.lockedBody.dom.firstChild;
33841         }else{
33842             source = this.mainBody.dom.firstChild;
33843             colIndex -= locked;
33844         }
33845         return source.rows[rowIndex].childNodes[colIndex];
33846     },
33847
33848     getCellText : function(rowIndex, colIndex){
33849         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33850     },
33851
33852     getCellBox : function(cell){
33853         var b = this.fly(cell).getBox();
33854         if(Roo.isOpera){ // opera fails to report the Y
33855             b.y = cell.offsetTop + this.mainBody.getY();
33856         }
33857         return b;
33858     },
33859
33860     getCellIndex : function(cell){
33861         var id = String(cell.className).match(this.cellRE);
33862         if(id){
33863             return parseInt(id[1], 10);
33864         }
33865         return 0;
33866     },
33867
33868     findHeaderIndex : function(n){
33869         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33870         return r ? this.getCellIndex(r) : false;
33871     },
33872
33873     findHeaderCell : function(n){
33874         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33875         return r ? r : false;
33876     },
33877
33878     findRowIndex : function(n){
33879         if(!n){
33880             return false;
33881         }
33882         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33883         return r ? r.rowIndex : false;
33884     },
33885
33886     findCellIndex : function(node){
33887         var stop = this.el.dom;
33888         while(node && node != stop){
33889             if(this.findRE.test(node.className)){
33890                 return this.getCellIndex(node);
33891             }
33892             node = node.parentNode;
33893         }
33894         return false;
33895     },
33896
33897     getColumnId : function(index){
33898         return this.cm.getColumnId(index);
33899     },
33900
33901     getSplitters : function()
33902     {
33903         if(this.splitterSelector){
33904            return Roo.DomQuery.select(this.splitterSelector);
33905         }else{
33906             return null;
33907       }
33908     },
33909
33910     getSplitter : function(index){
33911         return this.getSplitters()[index];
33912     },
33913
33914     onRowOver : function(e, t){
33915         var row;
33916         if((row = this.findRowIndex(t)) !== false){
33917             this.getRowComposite(row).addClass("x-grid-row-over");
33918         }
33919     },
33920
33921     onRowOut : function(e, t){
33922         var row;
33923         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33924             this.getRowComposite(row).removeClass("x-grid-row-over");
33925         }
33926     },
33927
33928     renderHeaders : function(){
33929         var cm = this.cm;
33930         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33931         var cb = [], lb = [], sb = [], lsb = [], p = {};
33932         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33933             p.cellId = "x-grid-hd-0-" + i;
33934             p.splitId = "x-grid-csplit-0-" + i;
33935             p.id = cm.getColumnId(i);
33936             p.value = cm.getColumnHeader(i) || "";
33937             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33938             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33939             if(!cm.isLocked(i)){
33940                 cb[cb.length] = ct.apply(p);
33941                 sb[sb.length] = st.apply(p);
33942             }else{
33943                 lb[lb.length] = ct.apply(p);
33944                 lsb[lsb.length] = st.apply(p);
33945             }
33946         }
33947         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33948                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33949     },
33950
33951     updateHeaders : function(){
33952         var html = this.renderHeaders();
33953         this.lockedHd.update(html[0]);
33954         this.mainHd.update(html[1]);
33955     },
33956
33957     /**
33958      * Focuses the specified row.
33959      * @param {Number} row The row index
33960      */
33961     focusRow : function(row)
33962     {
33963         //Roo.log('GridView.focusRow');
33964         var x = this.scroller.dom.scrollLeft;
33965         this.focusCell(row, 0, false);
33966         this.scroller.dom.scrollLeft = x;
33967     },
33968
33969     /**
33970      * Focuses the specified cell.
33971      * @param {Number} row The row index
33972      * @param {Number} col The column index
33973      * @param {Boolean} hscroll false to disable horizontal scrolling
33974      */
33975     focusCell : function(row, col, hscroll)
33976     {
33977         //Roo.log('GridView.focusCell');
33978         var el = this.ensureVisible(row, col, hscroll);
33979         this.focusEl.alignTo(el, "tl-tl");
33980         if(Roo.isGecko){
33981             this.focusEl.focus();
33982         }else{
33983             this.focusEl.focus.defer(1, this.focusEl);
33984         }
33985     },
33986
33987     /**
33988      * Scrolls the specified cell into view
33989      * @param {Number} row The row index
33990      * @param {Number} col The column index
33991      * @param {Boolean} hscroll false to disable horizontal scrolling
33992      */
33993     ensureVisible : function(row, col, hscroll)
33994     {
33995         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33996         //return null; //disable for testing.
33997         if(typeof row != "number"){
33998             row = row.rowIndex;
33999         }
34000         if(row < 0 && row >= this.ds.getCount()){
34001             return  null;
34002         }
34003         col = (col !== undefined ? col : 0);
34004         var cm = this.grid.colModel;
34005         while(cm.isHidden(col)){
34006             col++;
34007         }
34008
34009         var el = this.getCell(row, col);
34010         if(!el){
34011             return null;
34012         }
34013         var c = this.scroller.dom;
34014
34015         var ctop = parseInt(el.offsetTop, 10);
34016         var cleft = parseInt(el.offsetLeft, 10);
34017         var cbot = ctop + el.offsetHeight;
34018         var cright = cleft + el.offsetWidth;
34019         
34020         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
34021         var stop = parseInt(c.scrollTop, 10);
34022         var sleft = parseInt(c.scrollLeft, 10);
34023         var sbot = stop + ch;
34024         var sright = sleft + c.clientWidth;
34025         /*
34026         Roo.log('GridView.ensureVisible:' +
34027                 ' ctop:' + ctop +
34028                 ' c.clientHeight:' + c.clientHeight +
34029                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
34030                 ' stop:' + stop +
34031                 ' cbot:' + cbot +
34032                 ' sbot:' + sbot +
34033                 ' ch:' + ch  
34034                 );
34035         */
34036         if(ctop < stop){
34037              c.scrollTop = ctop;
34038             //Roo.log("set scrolltop to ctop DISABLE?");
34039         }else if(cbot > sbot){
34040             //Roo.log("set scrolltop to cbot-ch");
34041             c.scrollTop = cbot-ch;
34042         }
34043         
34044         if(hscroll !== false){
34045             if(cleft < sleft){
34046                 c.scrollLeft = cleft;
34047             }else if(cright > sright){
34048                 c.scrollLeft = cright-c.clientWidth;
34049             }
34050         }
34051          
34052         return el;
34053     },
34054
34055     updateColumns : function(){
34056         this.grid.stopEditing();
34057         var cm = this.grid.colModel, colIds = this.getColumnIds();
34058         //var totalWidth = cm.getTotalWidth();
34059         var pos = 0;
34060         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34061             //if(cm.isHidden(i)) continue;
34062             var w = cm.getColumnWidth(i);
34063             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34064             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
34065         }
34066         this.updateSplitters();
34067     },
34068
34069     generateRules : function(cm){
34070         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
34071         Roo.util.CSS.removeStyleSheet(rulesId);
34072         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34073             var cid = cm.getColumnId(i);
34074             var align = '';
34075             if(cm.config[i].align){
34076                 align = 'text-align:'+cm.config[i].align+';';
34077             }
34078             var hidden = '';
34079             if(cm.isHidden(i)){
34080                 hidden = 'display:none;';
34081             }
34082             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
34083             ruleBuf.push(
34084                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
34085                     this.hdSelector, cid, " {\n", align, width, "}\n",
34086                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
34087                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
34088         }
34089         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
34090     },
34091
34092     updateSplitters : function(){
34093         var cm = this.cm, s = this.getSplitters();
34094         if(s){ // splitters not created yet
34095             var pos = 0, locked = true;
34096             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
34097                 if(cm.isHidden(i)) {
34098                     continue;
34099                 }
34100                 var w = cm.getColumnWidth(i); // make sure it's a number
34101                 if(!cm.isLocked(i) && locked){
34102                     pos = 0;
34103                     locked = false;
34104                 }
34105                 pos += w;
34106                 s[i].style.left = (pos-this.splitOffset) + "px";
34107             }
34108         }
34109     },
34110
34111     handleHiddenChange : function(colModel, colIndex, hidden){
34112         if(hidden){
34113             this.hideColumn(colIndex);
34114         }else{
34115             this.unhideColumn(colIndex);
34116         }
34117     },
34118
34119     hideColumn : function(colIndex){
34120         var cid = this.getColumnId(colIndex);
34121         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
34122         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
34123         if(Roo.isSafari){
34124             this.updateHeaders();
34125         }
34126         this.updateSplitters();
34127         this.layout();
34128     },
34129
34130     unhideColumn : function(colIndex){
34131         var cid = this.getColumnId(colIndex);
34132         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
34133         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
34134
34135         if(Roo.isSafari){
34136             this.updateHeaders();
34137         }
34138         this.updateSplitters();
34139         this.layout();
34140     },
34141
34142     insertRows : function(dm, firstRow, lastRow, isUpdate){
34143         if(firstRow == 0 && lastRow == dm.getCount()-1){
34144             this.refresh();
34145         }else{
34146             if(!isUpdate){
34147                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
34148             }
34149             var s = this.getScrollState();
34150             var markup = this.renderRows(firstRow, lastRow);
34151             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
34152             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
34153             this.restoreScroll(s);
34154             if(!isUpdate){
34155                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
34156                 this.syncRowHeights(firstRow, lastRow);
34157                 this.stripeRows(firstRow);
34158                 this.layout();
34159             }
34160         }
34161     },
34162
34163     bufferRows : function(markup, target, index){
34164         var before = null, trows = target.rows, tbody = target.tBodies[0];
34165         if(index < trows.length){
34166             before = trows[index];
34167         }
34168         var b = document.createElement("div");
34169         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
34170         var rows = b.firstChild.rows;
34171         for(var i = 0, len = rows.length; i < len; i++){
34172             if(before){
34173                 tbody.insertBefore(rows[0], before);
34174             }else{
34175                 tbody.appendChild(rows[0]);
34176             }
34177         }
34178         b.innerHTML = "";
34179         b = null;
34180     },
34181
34182     deleteRows : function(dm, firstRow, lastRow){
34183         if(dm.getRowCount()<1){
34184             this.fireEvent("beforerefresh", this);
34185             this.mainBody.update("");
34186             this.lockedBody.update("");
34187             this.fireEvent("refresh", this);
34188         }else{
34189             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
34190             var bt = this.getBodyTable();
34191             var tbody = bt.firstChild;
34192             var rows = bt.rows;
34193             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
34194                 tbody.removeChild(rows[firstRow]);
34195             }
34196             this.stripeRows(firstRow);
34197             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
34198         }
34199     },
34200
34201     updateRows : function(dataSource, firstRow, lastRow){
34202         var s = this.getScrollState();
34203         this.refresh();
34204         this.restoreScroll(s);
34205     },
34206
34207     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
34208         if(!noRefresh){
34209            this.refresh();
34210         }
34211         this.updateHeaderSortState();
34212     },
34213
34214     getScrollState : function(){
34215         
34216         var sb = this.scroller.dom;
34217         return {left: sb.scrollLeft, top: sb.scrollTop};
34218     },
34219
34220     stripeRows : function(startRow){
34221         if(!this.grid.stripeRows || this.ds.getCount() < 1){
34222             return;
34223         }
34224         startRow = startRow || 0;
34225         var rows = this.getBodyTable().rows;
34226         var lrows = this.getLockedTable().rows;
34227         var cls = ' x-grid-row-alt ';
34228         for(var i = startRow, len = rows.length; i < len; i++){
34229             var row = rows[i], lrow = lrows[i];
34230             var isAlt = ((i+1) % 2 == 0);
34231             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
34232             if(isAlt == hasAlt){
34233                 continue;
34234             }
34235             if(isAlt){
34236                 row.className += " x-grid-row-alt";
34237             }else{
34238                 row.className = row.className.replace("x-grid-row-alt", "");
34239             }
34240             if(lrow){
34241                 lrow.className = row.className;
34242             }
34243         }
34244     },
34245
34246     restoreScroll : function(state){
34247         //Roo.log('GridView.restoreScroll');
34248         var sb = this.scroller.dom;
34249         sb.scrollLeft = state.left;
34250         sb.scrollTop = state.top;
34251         this.syncScroll();
34252     },
34253
34254     syncScroll : function(){
34255         //Roo.log('GridView.syncScroll');
34256         var sb = this.scroller.dom;
34257         var sh = this.mainHd.dom;
34258         var bs = this.mainBody.dom;
34259         var lv = this.lockedBody.dom;
34260         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
34261         lv.scrollTop = bs.scrollTop = sb.scrollTop;
34262     },
34263
34264     handleScroll : function(e){
34265         this.syncScroll();
34266         var sb = this.scroller.dom;
34267         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
34268         e.stopEvent();
34269     },
34270
34271     handleWheel : function(e){
34272         var d = e.getWheelDelta();
34273         this.scroller.dom.scrollTop -= d*22;
34274         // set this here to prevent jumpy scrolling on large tables
34275         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
34276         e.stopEvent();
34277     },
34278
34279     renderRows : function(startRow, endRow){
34280         // pull in all the crap needed to render rows
34281         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
34282         var colCount = cm.getColumnCount();
34283
34284         if(ds.getCount() < 1){
34285             return ["", ""];
34286         }
34287
34288         // build a map for all the columns
34289         var cs = [];
34290         for(var i = 0; i < colCount; i++){
34291             var name = cm.getDataIndex(i);
34292             cs[i] = {
34293                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
34294                 renderer : cm.getRenderer(i),
34295                 id : cm.getColumnId(i),
34296                 locked : cm.isLocked(i),
34297                 has_editor : cm.isCellEditable(i)
34298             };
34299         }
34300
34301         startRow = startRow || 0;
34302         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34303
34304         // records to render
34305         var rs = ds.getRange(startRow, endRow);
34306
34307         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34308     },
34309
34310     // As much as I hate to duplicate code, this was branched because FireFox really hates
34311     // [].join("") on strings. The performance difference was substantial enough to
34312     // branch this function
34313     doRender : Roo.isGecko ?
34314             function(cs, rs, ds, startRow, colCount, stripe){
34315                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34316                 // buffers
34317                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34318                 
34319                 var hasListener = this.grid.hasListener('rowclass');
34320                 var rowcfg = {};
34321                 for(var j = 0, len = rs.length; j < len; j++){
34322                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34323                     for(var i = 0; i < colCount; i++){
34324                         c = cs[i];
34325                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34326                         p.id = c.id;
34327                         p.css = p.attr = "";
34328                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34329                         if(p.value == undefined || p.value === "") {
34330                             p.value = "&#160;";
34331                         }
34332                         if(c.has_editor){
34333                             p.css += ' x-grid-editable-cell';
34334                         }
34335                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34336                             p.css +=  ' x-grid-dirty-cell';
34337                         }
34338                         var markup = ct.apply(p);
34339                         if(!c.locked){
34340                             cb+= markup;
34341                         }else{
34342                             lcb+= markup;
34343                         }
34344                     }
34345                     var alt = [];
34346                     if(stripe && ((rowIndex+1) % 2 == 0)){
34347                         alt.push("x-grid-row-alt")
34348                     }
34349                     if(r.dirty){
34350                         alt.push(  " x-grid-dirty-row");
34351                     }
34352                     rp.cells = lcb;
34353                     if(this.getRowClass){
34354                         alt.push(this.getRowClass(r, rowIndex));
34355                     }
34356                     if (hasListener) {
34357                         rowcfg = {
34358                              
34359                             record: r,
34360                             rowIndex : rowIndex,
34361                             rowClass : ''
34362                         };
34363                         this.grid.fireEvent('rowclass', this, rowcfg);
34364                         alt.push(rowcfg.rowClass);
34365                     }
34366                     rp.alt = alt.join(" ");
34367                     lbuf+= rt.apply(rp);
34368                     rp.cells = cb;
34369                     buf+=  rt.apply(rp);
34370                 }
34371                 return [lbuf, buf];
34372             } :
34373             function(cs, rs, ds, startRow, colCount, stripe){
34374                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34375                 // buffers
34376                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34377                 var hasListener = this.grid.hasListener('rowclass');
34378  
34379                 var rowcfg = {};
34380                 for(var j = 0, len = rs.length; j < len; j++){
34381                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34382                     for(var i = 0; i < colCount; i++){
34383                         c = cs[i];
34384                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34385                         p.id = c.id;
34386                         p.css = p.attr = "";
34387                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34388                         if(p.value == undefined || p.value === "") {
34389                             p.value = "&#160;";
34390                         }
34391                         //Roo.log(c);
34392                          if(c.has_editor){
34393                             p.css += ' x-grid-editable-cell';
34394                         }
34395                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34396                             p.css += ' x-grid-dirty-cell' 
34397                         }
34398                         
34399                         var markup = ct.apply(p);
34400                         if(!c.locked){
34401                             cb[cb.length] = markup;
34402                         }else{
34403                             lcb[lcb.length] = markup;
34404                         }
34405                     }
34406                     var alt = [];
34407                     if(stripe && ((rowIndex+1) % 2 == 0)){
34408                         alt.push( "x-grid-row-alt");
34409                     }
34410                     if(r.dirty){
34411                         alt.push(" x-grid-dirty-row");
34412                     }
34413                     rp.cells = lcb;
34414                     if(this.getRowClass){
34415                         alt.push( this.getRowClass(r, rowIndex));
34416                     }
34417                     if (hasListener) {
34418                         rowcfg = {
34419                              
34420                             record: r,
34421                             rowIndex : rowIndex,
34422                             rowClass : ''
34423                         };
34424                         this.grid.fireEvent('rowclass', this, rowcfg);
34425                         alt.push(rowcfg.rowClass);
34426                     }
34427                     
34428                     rp.alt = alt.join(" ");
34429                     rp.cells = lcb.join("");
34430                     lbuf[lbuf.length] = rt.apply(rp);
34431                     rp.cells = cb.join("");
34432                     buf[buf.length] =  rt.apply(rp);
34433                 }
34434                 return [lbuf.join(""), buf.join("")];
34435             },
34436
34437     renderBody : function(){
34438         var markup = this.renderRows();
34439         var bt = this.templates.body;
34440         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34441     },
34442
34443     /**
34444      * Refreshes the grid
34445      * @param {Boolean} headersToo
34446      */
34447     refresh : function(headersToo){
34448         this.fireEvent("beforerefresh", this);
34449         this.grid.stopEditing();
34450         var result = this.renderBody();
34451         this.lockedBody.update(result[0]);
34452         this.mainBody.update(result[1]);
34453         if(headersToo === true){
34454             this.updateHeaders();
34455             this.updateColumns();
34456             this.updateSplitters();
34457             this.updateHeaderSortState();
34458         }
34459         this.syncRowHeights();
34460         this.layout();
34461         this.fireEvent("refresh", this);
34462     },
34463
34464     handleColumnMove : function(cm, oldIndex, newIndex){
34465         this.indexMap = null;
34466         var s = this.getScrollState();
34467         this.refresh(true);
34468         this.restoreScroll(s);
34469         this.afterMove(newIndex);
34470     },
34471
34472     afterMove : function(colIndex){
34473         if(this.enableMoveAnim && Roo.enableFx){
34474             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34475         }
34476         // if multisort - fix sortOrder, and reload..
34477         if (this.grid.dataSource.multiSort) {
34478             // the we can call sort again..
34479             var dm = this.grid.dataSource;
34480             var cm = this.grid.colModel;
34481             var so = [];
34482             for(var i = 0; i < cm.config.length; i++ ) {
34483                 
34484                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34485                     continue; // dont' bother, it's not in sort list or being set.
34486                 }
34487                 
34488                 so.push(cm.config[i].dataIndex);
34489             };
34490             dm.sortOrder = so;
34491             dm.load(dm.lastOptions);
34492             
34493             
34494         }
34495         
34496     },
34497
34498     updateCell : function(dm, rowIndex, dataIndex){
34499         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34500         if(typeof colIndex == "undefined"){ // not present in grid
34501             return;
34502         }
34503         var cm = this.grid.colModel;
34504         var cell = this.getCell(rowIndex, colIndex);
34505         var cellText = this.getCellText(rowIndex, colIndex);
34506
34507         var p = {
34508             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34509             id : cm.getColumnId(colIndex),
34510             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34511         };
34512         var renderer = cm.getRenderer(colIndex);
34513         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34514         if(typeof val == "undefined" || val === "") {
34515             val = "&#160;";
34516         }
34517         cellText.innerHTML = val;
34518         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34519         this.syncRowHeights(rowIndex, rowIndex);
34520     },
34521
34522     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34523         var maxWidth = 0;
34524         if(this.grid.autoSizeHeaders){
34525             var h = this.getHeaderCellMeasure(colIndex);
34526             maxWidth = Math.max(maxWidth, h.scrollWidth);
34527         }
34528         var tb, index;
34529         if(this.cm.isLocked(colIndex)){
34530             tb = this.getLockedTable();
34531             index = colIndex;
34532         }else{
34533             tb = this.getBodyTable();
34534             index = colIndex - this.cm.getLockedCount();
34535         }
34536         if(tb && tb.rows){
34537             var rows = tb.rows;
34538             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34539             for(var i = 0; i < stopIndex; i++){
34540                 var cell = rows[i].childNodes[index].firstChild;
34541                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34542             }
34543         }
34544         return maxWidth + /*margin for error in IE*/ 5;
34545     },
34546     /**
34547      * Autofit a column to its content.
34548      * @param {Number} colIndex
34549      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34550      */
34551      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34552          if(this.cm.isHidden(colIndex)){
34553              return; // can't calc a hidden column
34554          }
34555         if(forceMinSize){
34556             var cid = this.cm.getColumnId(colIndex);
34557             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34558            if(this.grid.autoSizeHeaders){
34559                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34560            }
34561         }
34562         var newWidth = this.calcColumnWidth(colIndex);
34563         this.cm.setColumnWidth(colIndex,
34564             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34565         if(!suppressEvent){
34566             this.grid.fireEvent("columnresize", colIndex, newWidth);
34567         }
34568     },
34569
34570     /**
34571      * Autofits all columns to their content and then expands to fit any extra space in the grid
34572      */
34573      autoSizeColumns : function(){
34574         var cm = this.grid.colModel;
34575         var colCount = cm.getColumnCount();
34576         for(var i = 0; i < colCount; i++){
34577             this.autoSizeColumn(i, true, true);
34578         }
34579         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34580             this.fitColumns();
34581         }else{
34582             this.updateColumns();
34583             this.layout();
34584         }
34585     },
34586
34587     /**
34588      * Autofits all columns to the grid's width proportionate with their current size
34589      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34590      */
34591     fitColumns : function(reserveScrollSpace){
34592         var cm = this.grid.colModel;
34593         var colCount = cm.getColumnCount();
34594         var cols = [];
34595         var width = 0;
34596         var i, w;
34597         for (i = 0; i < colCount; i++){
34598             if(!cm.isHidden(i) && !cm.isFixed(i)){
34599                 w = cm.getColumnWidth(i);
34600                 cols.push(i);
34601                 cols.push(w);
34602                 width += w;
34603             }
34604         }
34605         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34606         if(reserveScrollSpace){
34607             avail -= 17;
34608         }
34609         var frac = (avail - cm.getTotalWidth())/width;
34610         while (cols.length){
34611             w = cols.pop();
34612             i = cols.pop();
34613             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34614         }
34615         this.updateColumns();
34616         this.layout();
34617     },
34618
34619     onRowSelect : function(rowIndex){
34620         var row = this.getRowComposite(rowIndex);
34621         row.addClass("x-grid-row-selected");
34622     },
34623
34624     onRowDeselect : function(rowIndex){
34625         var row = this.getRowComposite(rowIndex);
34626         row.removeClass("x-grid-row-selected");
34627     },
34628
34629     onCellSelect : function(row, col){
34630         var cell = this.getCell(row, col);
34631         if(cell){
34632             Roo.fly(cell).addClass("x-grid-cell-selected");
34633         }
34634     },
34635
34636     onCellDeselect : function(row, col){
34637         var cell = this.getCell(row, col);
34638         if(cell){
34639             Roo.fly(cell).removeClass("x-grid-cell-selected");
34640         }
34641     },
34642
34643     updateHeaderSortState : function(){
34644         
34645         // sort state can be single { field: xxx, direction : yyy}
34646         // or   { xxx=>ASC , yyy : DESC ..... }
34647         
34648         var mstate = {};
34649         if (!this.ds.multiSort) { 
34650             var state = this.ds.getSortState();
34651             if(!state){
34652                 return;
34653             }
34654             mstate[state.field] = state.direction;
34655             // FIXME... - this is not used here.. but might be elsewhere..
34656             this.sortState = state;
34657             
34658         } else {
34659             mstate = this.ds.sortToggle;
34660         }
34661         //remove existing sort classes..
34662         
34663         var sc = this.sortClasses;
34664         var hds = this.el.select(this.headerSelector).removeClass(sc);
34665         
34666         for(var f in mstate) {
34667         
34668             var sortColumn = this.cm.findColumnIndex(f);
34669             
34670             if(sortColumn != -1){
34671                 var sortDir = mstate[f];        
34672                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34673             }
34674         }
34675         
34676          
34677         
34678     },
34679
34680
34681     handleHeaderClick : function(g, index,e){
34682         
34683         Roo.log("header click");
34684         
34685         if (Roo.isTouch) {
34686             // touch events on header are handled by context
34687             this.handleHdCtx(g,index,e);
34688             return;
34689         }
34690         
34691         
34692         if(this.headersDisabled){
34693             return;
34694         }
34695         var dm = g.dataSource, cm = g.colModel;
34696         if(!cm.isSortable(index)){
34697             return;
34698         }
34699         g.stopEditing();
34700         
34701         if (dm.multiSort) {
34702             // update the sortOrder
34703             var so = [];
34704             for(var i = 0; i < cm.config.length; i++ ) {
34705                 
34706                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34707                     continue; // dont' bother, it's not in sort list or being set.
34708                 }
34709                 
34710                 so.push(cm.config[i].dataIndex);
34711             };
34712             dm.sortOrder = so;
34713         }
34714         
34715         
34716         dm.sort(cm.getDataIndex(index));
34717     },
34718
34719
34720     destroy : function(){
34721         if(this.colMenu){
34722             this.colMenu.removeAll();
34723             Roo.menu.MenuMgr.unregister(this.colMenu);
34724             this.colMenu.getEl().remove();
34725             delete this.colMenu;
34726         }
34727         if(this.hmenu){
34728             this.hmenu.removeAll();
34729             Roo.menu.MenuMgr.unregister(this.hmenu);
34730             this.hmenu.getEl().remove();
34731             delete this.hmenu;
34732         }
34733         if(this.grid.enableColumnMove){
34734             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34735             if(dds){
34736                 for(var dd in dds){
34737                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34738                         var elid = dds[dd].dragElId;
34739                         dds[dd].unreg();
34740                         Roo.get(elid).remove();
34741                     } else if(dds[dd].config.isTarget){
34742                         dds[dd].proxyTop.remove();
34743                         dds[dd].proxyBottom.remove();
34744                         dds[dd].unreg();
34745                     }
34746                     if(Roo.dd.DDM.locationCache[dd]){
34747                         delete Roo.dd.DDM.locationCache[dd];
34748                     }
34749                 }
34750                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34751             }
34752         }
34753         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34754         this.bind(null, null);
34755         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34756     },
34757
34758     handleLockChange : function(){
34759         this.refresh(true);
34760     },
34761
34762     onDenyColumnLock : function(){
34763
34764     },
34765
34766     onDenyColumnHide : function(){
34767
34768     },
34769
34770     handleHdMenuClick : function(item){
34771         var index = this.hdCtxIndex;
34772         var cm = this.cm, ds = this.ds;
34773         switch(item.id){
34774             case "asc":
34775                 ds.sort(cm.getDataIndex(index), "ASC");
34776                 break;
34777             case "desc":
34778                 ds.sort(cm.getDataIndex(index), "DESC");
34779                 break;
34780             case "lock":
34781                 var lc = cm.getLockedCount();
34782                 if(cm.getColumnCount(true) <= lc+1){
34783                     this.onDenyColumnLock();
34784                     return;
34785                 }
34786                 if(lc != index){
34787                     cm.setLocked(index, true, true);
34788                     cm.moveColumn(index, lc);
34789                     this.grid.fireEvent("columnmove", index, lc);
34790                 }else{
34791                     cm.setLocked(index, true);
34792                 }
34793             break;
34794             case "unlock":
34795                 var lc = cm.getLockedCount();
34796                 if((lc-1) != index){
34797                     cm.setLocked(index, false, true);
34798                     cm.moveColumn(index, lc-1);
34799                     this.grid.fireEvent("columnmove", index, lc-1);
34800                 }else{
34801                     cm.setLocked(index, false);
34802                 }
34803             break;
34804             case 'wider': // used to expand cols on touch..
34805             case 'narrow':
34806                 var cw = cm.getColumnWidth(index);
34807                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34808                 cw = Math.max(0, cw);
34809                 cw = Math.min(cw,4000);
34810                 cm.setColumnWidth(index, cw);
34811                 break;
34812                 
34813             default:
34814                 index = cm.getIndexById(item.id.substr(4));
34815                 if(index != -1){
34816                     if(item.checked && cm.getColumnCount(true) <= 1){
34817                         this.onDenyColumnHide();
34818                         return false;
34819                     }
34820                     cm.setHidden(index, item.checked);
34821                 }
34822         }
34823         return true;
34824     },
34825
34826     beforeColMenuShow : function(){
34827         var cm = this.cm,  colCount = cm.getColumnCount();
34828         this.colMenu.removeAll();
34829         for(var i = 0; i < colCount; i++){
34830             this.colMenu.add(new Roo.menu.CheckItem({
34831                 id: "col-"+cm.getColumnId(i),
34832                 text: cm.getColumnHeader(i),
34833                 checked: !cm.isHidden(i),
34834                 hideOnClick:false
34835             }));
34836         }
34837     },
34838
34839     handleHdCtx : function(g, index, e){
34840         e.stopEvent();
34841         var hd = this.getHeaderCell(index);
34842         this.hdCtxIndex = index;
34843         var ms = this.hmenu.items, cm = this.cm;
34844         ms.get("asc").setDisabled(!cm.isSortable(index));
34845         ms.get("desc").setDisabled(!cm.isSortable(index));
34846         if(this.grid.enableColLock !== false){
34847             ms.get("lock").setDisabled(cm.isLocked(index));
34848             ms.get("unlock").setDisabled(!cm.isLocked(index));
34849         }
34850         this.hmenu.show(hd, "tl-bl");
34851     },
34852
34853     handleHdOver : function(e){
34854         var hd = this.findHeaderCell(e.getTarget());
34855         if(hd && !this.headersDisabled){
34856             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34857                this.fly(hd).addClass("x-grid-hd-over");
34858             }
34859         }
34860     },
34861
34862     handleHdOut : function(e){
34863         var hd = this.findHeaderCell(e.getTarget());
34864         if(hd){
34865             this.fly(hd).removeClass("x-grid-hd-over");
34866         }
34867     },
34868
34869     handleSplitDblClick : function(e, t){
34870         var i = this.getCellIndex(t);
34871         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34872             this.autoSizeColumn(i, true);
34873             this.layout();
34874         }
34875     },
34876
34877     render : function(){
34878
34879         var cm = this.cm;
34880         var colCount = cm.getColumnCount();
34881
34882         if(this.grid.monitorWindowResize === true){
34883             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34884         }
34885         var header = this.renderHeaders();
34886         var body = this.templates.body.apply({rows:""});
34887         var html = this.templates.master.apply({
34888             lockedBody: body,
34889             body: body,
34890             lockedHeader: header[0],
34891             header: header[1]
34892         });
34893
34894         //this.updateColumns();
34895
34896         this.grid.getGridEl().dom.innerHTML = html;
34897
34898         this.initElements();
34899         
34900         // a kludge to fix the random scolling effect in webkit
34901         this.el.on("scroll", function() {
34902             this.el.dom.scrollTop=0; // hopefully not recursive..
34903         },this);
34904
34905         this.scroller.on("scroll", this.handleScroll, this);
34906         this.lockedBody.on("mousewheel", this.handleWheel, this);
34907         this.mainBody.on("mousewheel", this.handleWheel, this);
34908
34909         this.mainHd.on("mouseover", this.handleHdOver, this);
34910         this.mainHd.on("mouseout", this.handleHdOut, this);
34911         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34912                 {delegate: "."+this.splitClass});
34913
34914         this.lockedHd.on("mouseover", this.handleHdOver, this);
34915         this.lockedHd.on("mouseout", this.handleHdOut, this);
34916         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34917                 {delegate: "."+this.splitClass});
34918
34919         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34920             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34921         }
34922
34923         this.updateSplitters();
34924
34925         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34926             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34927             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34928         }
34929
34930         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34931             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34932             this.hmenu.add(
34933                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34934                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34935             );
34936             if(this.grid.enableColLock !== false){
34937                 this.hmenu.add('-',
34938                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34939                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34940                 );
34941             }
34942             if (Roo.isTouch) {
34943                  this.hmenu.add('-',
34944                     {id:"wider", text: this.columnsWiderText},
34945                     {id:"narrow", text: this.columnsNarrowText }
34946                 );
34947                 
34948                  
34949             }
34950             
34951             if(this.grid.enableColumnHide !== false){
34952
34953                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34954                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34955                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34956
34957                 this.hmenu.add('-',
34958                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34959                 );
34960             }
34961             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34962
34963             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34964         }
34965
34966         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34967             this.dd = new Roo.grid.GridDragZone(this.grid, {
34968                 ddGroup : this.grid.ddGroup || 'GridDD'
34969             });
34970             
34971         }
34972
34973         /*
34974         for(var i = 0; i < colCount; i++){
34975             if(cm.isHidden(i)){
34976                 this.hideColumn(i);
34977             }
34978             if(cm.config[i].align){
34979                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34980                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34981             }
34982         }*/
34983         
34984         this.updateHeaderSortState();
34985
34986         this.beforeInitialResize();
34987         this.layout(true);
34988
34989         // two part rendering gives faster view to the user
34990         this.renderPhase2.defer(1, this);
34991     },
34992
34993     renderPhase2 : function(){
34994         // render the rows now
34995         this.refresh();
34996         if(this.grid.autoSizeColumns){
34997             this.autoSizeColumns();
34998         }
34999     },
35000
35001     beforeInitialResize : function(){
35002
35003     },
35004
35005     onColumnSplitterMoved : function(i, w){
35006         this.userResized = true;
35007         var cm = this.grid.colModel;
35008         cm.setColumnWidth(i, w, true);
35009         var cid = cm.getColumnId(i);
35010         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35011         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
35012         this.updateSplitters();
35013         this.layout();
35014         this.grid.fireEvent("columnresize", i, w);
35015     },
35016
35017     syncRowHeights : function(startIndex, endIndex){
35018         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
35019             startIndex = startIndex || 0;
35020             var mrows = this.getBodyTable().rows;
35021             var lrows = this.getLockedTable().rows;
35022             var len = mrows.length-1;
35023             endIndex = Math.min(endIndex || len, len);
35024             for(var i = startIndex; i <= endIndex; i++){
35025                 var m = mrows[i], l = lrows[i];
35026                 var h = Math.max(m.offsetHeight, l.offsetHeight);
35027                 m.style.height = l.style.height = h + "px";
35028             }
35029         }
35030     },
35031
35032     layout : function(initialRender, is2ndPass){
35033         var g = this.grid;
35034         var auto = g.autoHeight;
35035         var scrollOffset = 16;
35036         var c = g.getGridEl(), cm = this.cm,
35037                 expandCol = g.autoExpandColumn,
35038                 gv = this;
35039         //c.beginMeasure();
35040
35041         if(!c.dom.offsetWidth){ // display:none?
35042             if(initialRender){
35043                 this.lockedWrap.show();
35044                 this.mainWrap.show();
35045             }
35046             return;
35047         }
35048
35049         var hasLock = this.cm.isLocked(0);
35050
35051         var tbh = this.headerPanel.getHeight();
35052         var bbh = this.footerPanel.getHeight();
35053
35054         if(auto){
35055             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
35056             var newHeight = ch + c.getBorderWidth("tb");
35057             if(g.maxHeight){
35058                 newHeight = Math.min(g.maxHeight, newHeight);
35059             }
35060             c.setHeight(newHeight);
35061         }
35062
35063         if(g.autoWidth){
35064             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
35065         }
35066
35067         var s = this.scroller;
35068
35069         var csize = c.getSize(true);
35070
35071         this.el.setSize(csize.width, csize.height);
35072
35073         this.headerPanel.setWidth(csize.width);
35074         this.footerPanel.setWidth(csize.width);
35075
35076         var hdHeight = this.mainHd.getHeight();
35077         var vw = csize.width;
35078         var vh = csize.height - (tbh + bbh);
35079
35080         s.setSize(vw, vh);
35081
35082         var bt = this.getBodyTable();
35083         
35084         if(cm.getLockedCount() == cm.config.length){
35085             bt = this.getLockedTable();
35086         }
35087         
35088         var ltWidth = hasLock ?
35089                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
35090
35091         var scrollHeight = bt.offsetHeight;
35092         var scrollWidth = ltWidth + bt.offsetWidth;
35093         var vscroll = false, hscroll = false;
35094
35095         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
35096
35097         var lw = this.lockedWrap, mw = this.mainWrap;
35098         var lb = this.lockedBody, mb = this.mainBody;
35099
35100         setTimeout(function(){
35101             var t = s.dom.offsetTop;
35102             var w = s.dom.clientWidth,
35103                 h = s.dom.clientHeight;
35104
35105             lw.setTop(t);
35106             lw.setSize(ltWidth, h);
35107
35108             mw.setLeftTop(ltWidth, t);
35109             mw.setSize(w-ltWidth, h);
35110
35111             lb.setHeight(h-hdHeight);
35112             mb.setHeight(h-hdHeight);
35113
35114             if(is2ndPass !== true && !gv.userResized && expandCol){
35115                 // high speed resize without full column calculation
35116                 
35117                 var ci = cm.getIndexById(expandCol);
35118                 if (ci < 0) {
35119                     ci = cm.findColumnIndex(expandCol);
35120                 }
35121                 ci = Math.max(0, ci); // make sure it's got at least the first col.
35122                 var expandId = cm.getColumnId(ci);
35123                 var  tw = cm.getTotalWidth(false);
35124                 var currentWidth = cm.getColumnWidth(ci);
35125                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
35126                 if(currentWidth != cw){
35127                     cm.setColumnWidth(ci, cw, true);
35128                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35129                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
35130                     gv.updateSplitters();
35131                     gv.layout(false, true);
35132                 }
35133             }
35134
35135             if(initialRender){
35136                 lw.show();
35137                 mw.show();
35138             }
35139             //c.endMeasure();
35140         }, 10);
35141     },
35142
35143     onWindowResize : function(){
35144         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
35145             return;
35146         }
35147         this.layout();
35148     },
35149
35150     appendFooter : function(parentEl){
35151         return null;
35152     },
35153
35154     sortAscText : "Sort Ascending",
35155     sortDescText : "Sort Descending",
35156     lockText : "Lock Column",
35157     unlockText : "Unlock Column",
35158     columnsText : "Columns",
35159  
35160     columnsWiderText : "Wider",
35161     columnsNarrowText : "Thinner"
35162 });
35163
35164
35165 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
35166     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
35167     this.proxy.el.addClass('x-grid3-col-dd');
35168 };
35169
35170 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
35171     handleMouseDown : function(e){
35172
35173     },
35174
35175     callHandleMouseDown : function(e){
35176         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
35177     }
35178 });
35179 /*
35180  * Based on:
35181  * Ext JS Library 1.1.1
35182  * Copyright(c) 2006-2007, Ext JS, LLC.
35183  *
35184  * Originally Released Under LGPL - original licence link has changed is not relivant.
35185  *
35186  * Fork - LGPL
35187  * <script type="text/javascript">
35188  */
35189  
35190 // private
35191 // This is a support class used internally by the Grid components
35192 Roo.grid.SplitDragZone = function(grid, hd, hd2){
35193     this.grid = grid;
35194     this.view = grid.getView();
35195     this.proxy = this.view.resizeProxy;
35196     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
35197         "gridSplitters" + this.grid.getGridEl().id, {
35198         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
35199     });
35200     this.setHandleElId(Roo.id(hd));
35201     this.setOuterHandleElId(Roo.id(hd2));
35202     this.scroll = false;
35203 };
35204 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
35205     fly: Roo.Element.fly,
35206
35207     b4StartDrag : function(x, y){
35208         this.view.headersDisabled = true;
35209         this.proxy.setHeight(this.view.mainWrap.getHeight());
35210         var w = this.cm.getColumnWidth(this.cellIndex);
35211         var minw = Math.max(w-this.grid.minColumnWidth, 0);
35212         this.resetConstraints();
35213         this.setXConstraint(minw, 1000);
35214         this.setYConstraint(0, 0);
35215         this.minX = x - minw;
35216         this.maxX = x + 1000;
35217         this.startPos = x;
35218         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
35219     },
35220
35221
35222     handleMouseDown : function(e){
35223         ev = Roo.EventObject.setEvent(e);
35224         var t = this.fly(ev.getTarget());
35225         if(t.hasClass("x-grid-split")){
35226             this.cellIndex = this.view.getCellIndex(t.dom);
35227             this.split = t.dom;
35228             this.cm = this.grid.colModel;
35229             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
35230                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
35231             }
35232         }
35233     },
35234
35235     endDrag : function(e){
35236         this.view.headersDisabled = false;
35237         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
35238         var diff = endX - this.startPos;
35239         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
35240     },
35241
35242     autoOffset : function(){
35243         this.setDelta(0,0);
35244     }
35245 });/*
35246  * Based on:
35247  * Ext JS Library 1.1.1
35248  * Copyright(c) 2006-2007, Ext JS, LLC.
35249  *
35250  * Originally Released Under LGPL - original licence link has changed is not relivant.
35251  *
35252  * Fork - LGPL
35253  * <script type="text/javascript">
35254  */
35255  
35256 // private
35257 // This is a support class used internally by the Grid components
35258 Roo.grid.GridDragZone = function(grid, config){
35259     this.view = grid.getView();
35260     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
35261     if(this.view.lockedBody){
35262         this.setHandleElId(Roo.id(this.view.mainBody.dom));
35263         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
35264     }
35265     this.scroll = false;
35266     this.grid = grid;
35267     this.ddel = document.createElement('div');
35268     this.ddel.className = 'x-grid-dd-wrap';
35269 };
35270
35271 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
35272     ddGroup : "GridDD",
35273
35274     getDragData : function(e){
35275         var t = Roo.lib.Event.getTarget(e);
35276         var rowIndex = this.view.findRowIndex(t);
35277         var sm = this.grid.selModel;
35278             
35279         //Roo.log(rowIndex);
35280         
35281         if (sm.getSelectedCell) {
35282             // cell selection..
35283             if (!sm.getSelectedCell()) {
35284                 return false;
35285             }
35286             if (rowIndex != sm.getSelectedCell()[0]) {
35287                 return false;
35288             }
35289         
35290         }
35291         
35292         if(rowIndex !== false){
35293             
35294             // if editorgrid.. 
35295             
35296             
35297             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35298                
35299             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35300               //  
35301             //}
35302             if (e.hasModifier()){
35303                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35304             }
35305             
35306             Roo.log("getDragData");
35307             
35308             return {
35309                 grid: this.grid,
35310                 ddel: this.ddel,
35311                 rowIndex: rowIndex,
35312                 selections:sm.getSelections ? sm.getSelections() : (
35313                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35314                 )
35315             };
35316         }
35317         return false;
35318     },
35319
35320     onInitDrag : function(e){
35321         var data = this.dragData;
35322         this.ddel.innerHTML = this.grid.getDragDropText();
35323         this.proxy.update(this.ddel);
35324         // fire start drag?
35325     },
35326
35327     afterRepair : function(){
35328         this.dragging = false;
35329     },
35330
35331     getRepairXY : function(e, data){
35332         return false;
35333     },
35334
35335     onEndDrag : function(data, e){
35336         // fire end drag?
35337     },
35338
35339     onValidDrop : function(dd, e, id){
35340         // fire drag drop?
35341         this.hideProxy();
35342     },
35343
35344     beforeInvalidDrop : function(e, id){
35345
35346     }
35347 });/*
35348  * Based on:
35349  * Ext JS Library 1.1.1
35350  * Copyright(c) 2006-2007, Ext JS, LLC.
35351  *
35352  * Originally Released Under LGPL - original licence link has changed is not relivant.
35353  *
35354  * Fork - LGPL
35355  * <script type="text/javascript">
35356  */
35357  
35358
35359 /**
35360  * @class Roo.grid.ColumnModel
35361  * @extends Roo.util.Observable
35362  * This is the default implementation of a ColumnModel used by the Grid. It defines
35363  * the columns in the grid.
35364  * <br>Usage:<br>
35365  <pre><code>
35366  var colModel = new Roo.grid.ColumnModel([
35367         {header: "Ticker", width: 60, sortable: true, locked: true},
35368         {header: "Company Name", width: 150, sortable: true},
35369         {header: "Market Cap.", width: 100, sortable: true},
35370         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35371         {header: "Employees", width: 100, sortable: true, resizable: false}
35372  ]);
35373  </code></pre>
35374  * <p>
35375  
35376  * The config options listed for this class are options which may appear in each
35377  * individual column definition.
35378  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35379  * @constructor
35380  * @param {Object} config An Array of column config objects. See this class's
35381  * config objects for details.
35382 */
35383 Roo.grid.ColumnModel = function(config){
35384         /**
35385      * The config passed into the constructor
35386      */
35387     this.config = config;
35388     this.lookup = {};
35389
35390     // if no id, create one
35391     // if the column does not have a dataIndex mapping,
35392     // map it to the order it is in the config
35393     for(var i = 0, len = config.length; i < len; i++){
35394         var c = config[i];
35395         if(typeof c.dataIndex == "undefined"){
35396             c.dataIndex = i;
35397         }
35398         if(typeof c.renderer == "string"){
35399             c.renderer = Roo.util.Format[c.renderer];
35400         }
35401         if(typeof c.id == "undefined"){
35402             c.id = Roo.id();
35403         }
35404         if(c.editor && c.editor.xtype){
35405             c.editor  = Roo.factory(c.editor, Roo.grid);
35406         }
35407         if(c.editor && c.editor.isFormField){
35408             c.editor = new Roo.grid.GridEditor(c.editor);
35409         }
35410         this.lookup[c.id] = c;
35411     }
35412
35413     /**
35414      * The width of columns which have no width specified (defaults to 100)
35415      * @type Number
35416      */
35417     this.defaultWidth = 100;
35418
35419     /**
35420      * Default sortable of columns which have no sortable specified (defaults to false)
35421      * @type Boolean
35422      */
35423     this.defaultSortable = false;
35424
35425     this.addEvents({
35426         /**
35427              * @event widthchange
35428              * Fires when the width of a column changes.
35429              * @param {ColumnModel} this
35430              * @param {Number} columnIndex The column index
35431              * @param {Number} newWidth The new width
35432              */
35433             "widthchange": true,
35434         /**
35435              * @event headerchange
35436              * Fires when the text of a header changes.
35437              * @param {ColumnModel} this
35438              * @param {Number} columnIndex The column index
35439              * @param {Number} newText The new header text
35440              */
35441             "headerchange": true,
35442         /**
35443              * @event hiddenchange
35444              * Fires when a column is hidden or "unhidden".
35445              * @param {ColumnModel} this
35446              * @param {Number} columnIndex The column index
35447              * @param {Boolean} hidden true if hidden, false otherwise
35448              */
35449             "hiddenchange": true,
35450             /**
35451          * @event columnmoved
35452          * Fires when a column is moved.
35453          * @param {ColumnModel} this
35454          * @param {Number} oldIndex
35455          * @param {Number} newIndex
35456          */
35457         "columnmoved" : true,
35458         /**
35459          * @event columlockchange
35460          * Fires when a column's locked state is changed
35461          * @param {ColumnModel} this
35462          * @param {Number} colIndex
35463          * @param {Boolean} locked true if locked
35464          */
35465         "columnlockchange" : true
35466     });
35467     Roo.grid.ColumnModel.superclass.constructor.call(this);
35468 };
35469 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35470     /**
35471      * @cfg {String} header The header text to display in the Grid view.
35472      */
35473     /**
35474      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35475      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35476      * specified, the column's index is used as an index into the Record's data Array.
35477      */
35478     /**
35479      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35480      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35481      */
35482     /**
35483      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35484      * Defaults to the value of the {@link #defaultSortable} property.
35485      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35486      */
35487     /**
35488      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35489      */
35490     /**
35491      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35492      */
35493     /**
35494      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35495      */
35496     /**
35497      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35498      */
35499     /**
35500      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35501      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35502      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35503      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35504      */
35505        /**
35506      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35507      */
35508     /**
35509      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35510      */
35511     /**
35512      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35513      */
35514     /**
35515      * @cfg {String} cursor (Optional)
35516      */
35517     /**
35518      * @cfg {String} tooltip (Optional)
35519      */
35520     /**
35521      * @cfg {Number} xs (Optional)
35522      */
35523     /**
35524      * @cfg {Number} sm (Optional)
35525      */
35526     /**
35527      * @cfg {Number} md (Optional)
35528      */
35529     /**
35530      * @cfg {Number} lg (Optional)
35531      */
35532     /**
35533      * Returns the id of the column at the specified index.
35534      * @param {Number} index The column index
35535      * @return {String} the id
35536      */
35537     getColumnId : function(index){
35538         return this.config[index].id;
35539     },
35540
35541     /**
35542      * Returns the column for a specified id.
35543      * @param {String} id The column id
35544      * @return {Object} the column
35545      */
35546     getColumnById : function(id){
35547         return this.lookup[id];
35548     },
35549
35550     
35551     /**
35552      * Returns the column for a specified dataIndex.
35553      * @param {String} dataIndex The column dataIndex
35554      * @return {Object|Boolean} the column or false if not found
35555      */
35556     getColumnByDataIndex: function(dataIndex){
35557         var index = this.findColumnIndex(dataIndex);
35558         return index > -1 ? this.config[index] : false;
35559     },
35560     
35561     /**
35562      * Returns the index for a specified column id.
35563      * @param {String} id The column id
35564      * @return {Number} the index, or -1 if not found
35565      */
35566     getIndexById : function(id){
35567         for(var i = 0, len = this.config.length; i < len; i++){
35568             if(this.config[i].id == id){
35569                 return i;
35570             }
35571         }
35572         return -1;
35573     },
35574     
35575     /**
35576      * Returns the index for a specified column dataIndex.
35577      * @param {String} dataIndex The column dataIndex
35578      * @return {Number} the index, or -1 if not found
35579      */
35580     
35581     findColumnIndex : function(dataIndex){
35582         for(var i = 0, len = this.config.length; i < len; i++){
35583             if(this.config[i].dataIndex == dataIndex){
35584                 return i;
35585             }
35586         }
35587         return -1;
35588     },
35589     
35590     
35591     moveColumn : function(oldIndex, newIndex){
35592         var c = this.config[oldIndex];
35593         this.config.splice(oldIndex, 1);
35594         this.config.splice(newIndex, 0, c);
35595         this.dataMap = null;
35596         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35597     },
35598
35599     isLocked : function(colIndex){
35600         return this.config[colIndex].locked === true;
35601     },
35602
35603     setLocked : function(colIndex, value, suppressEvent){
35604         if(this.isLocked(colIndex) == value){
35605             return;
35606         }
35607         this.config[colIndex].locked = value;
35608         if(!suppressEvent){
35609             this.fireEvent("columnlockchange", this, colIndex, value);
35610         }
35611     },
35612
35613     getTotalLockedWidth : function(){
35614         var totalWidth = 0;
35615         for(var i = 0; i < this.config.length; i++){
35616             if(this.isLocked(i) && !this.isHidden(i)){
35617                 this.totalWidth += this.getColumnWidth(i);
35618             }
35619         }
35620         return totalWidth;
35621     },
35622
35623     getLockedCount : function(){
35624         for(var i = 0, len = this.config.length; i < len; i++){
35625             if(!this.isLocked(i)){
35626                 return i;
35627             }
35628         }
35629         
35630         return this.config.length;
35631     },
35632
35633     /**
35634      * Returns the number of columns.
35635      * @return {Number}
35636      */
35637     getColumnCount : function(visibleOnly){
35638         if(visibleOnly === true){
35639             var c = 0;
35640             for(var i = 0, len = this.config.length; i < len; i++){
35641                 if(!this.isHidden(i)){
35642                     c++;
35643                 }
35644             }
35645             return c;
35646         }
35647         return this.config.length;
35648     },
35649
35650     /**
35651      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35652      * @param {Function} fn
35653      * @param {Object} scope (optional)
35654      * @return {Array} result
35655      */
35656     getColumnsBy : function(fn, scope){
35657         var r = [];
35658         for(var i = 0, len = this.config.length; i < len; i++){
35659             var c = this.config[i];
35660             if(fn.call(scope||this, c, i) === true){
35661                 r[r.length] = c;
35662             }
35663         }
35664         return r;
35665     },
35666
35667     /**
35668      * Returns true if the specified column is sortable.
35669      * @param {Number} col The column index
35670      * @return {Boolean}
35671      */
35672     isSortable : function(col){
35673         if(typeof this.config[col].sortable == "undefined"){
35674             return this.defaultSortable;
35675         }
35676         return this.config[col].sortable;
35677     },
35678
35679     /**
35680      * Returns the rendering (formatting) function defined for the column.
35681      * @param {Number} col The column index.
35682      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35683      */
35684     getRenderer : function(col){
35685         if(!this.config[col].renderer){
35686             return Roo.grid.ColumnModel.defaultRenderer;
35687         }
35688         return this.config[col].renderer;
35689     },
35690
35691     /**
35692      * Sets the rendering (formatting) function for a column.
35693      * @param {Number} col The column index
35694      * @param {Function} fn The function to use to process the cell's raw data
35695      * to return HTML markup for the grid view. The render function is called with
35696      * the following parameters:<ul>
35697      * <li>Data value.</li>
35698      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35699      * <li>css A CSS style string to apply to the table cell.</li>
35700      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35701      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35702      * <li>Row index</li>
35703      * <li>Column index</li>
35704      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35705      */
35706     setRenderer : function(col, fn){
35707         this.config[col].renderer = fn;
35708     },
35709
35710     /**
35711      * Returns the width for the specified column.
35712      * @param {Number} col The column index
35713      * @return {Number}
35714      */
35715     getColumnWidth : function(col){
35716         return this.config[col].width * 1 || this.defaultWidth;
35717     },
35718
35719     /**
35720      * Sets the width for a column.
35721      * @param {Number} col The column index
35722      * @param {Number} width The new width
35723      */
35724     setColumnWidth : function(col, width, suppressEvent){
35725         this.config[col].width = width;
35726         this.totalWidth = null;
35727         if(!suppressEvent){
35728              this.fireEvent("widthchange", this, col, width);
35729         }
35730     },
35731
35732     /**
35733      * Returns the total width of all columns.
35734      * @param {Boolean} includeHidden True to include hidden column widths
35735      * @return {Number}
35736      */
35737     getTotalWidth : function(includeHidden){
35738         if(!this.totalWidth){
35739             this.totalWidth = 0;
35740             for(var i = 0, len = this.config.length; i < len; i++){
35741                 if(includeHidden || !this.isHidden(i)){
35742                     this.totalWidth += this.getColumnWidth(i);
35743                 }
35744             }
35745         }
35746         return this.totalWidth;
35747     },
35748
35749     /**
35750      * Returns the header for the specified column.
35751      * @param {Number} col The column index
35752      * @return {String}
35753      */
35754     getColumnHeader : function(col){
35755         return this.config[col].header;
35756     },
35757
35758     /**
35759      * Sets the header for a column.
35760      * @param {Number} col The column index
35761      * @param {String} header The new header
35762      */
35763     setColumnHeader : function(col, header){
35764         this.config[col].header = header;
35765         this.fireEvent("headerchange", this, col, header);
35766     },
35767
35768     /**
35769      * Returns the tooltip for the specified column.
35770      * @param {Number} col The column index
35771      * @return {String}
35772      */
35773     getColumnTooltip : function(col){
35774             return this.config[col].tooltip;
35775     },
35776     /**
35777      * Sets the tooltip for a column.
35778      * @param {Number} col The column index
35779      * @param {String} tooltip The new tooltip
35780      */
35781     setColumnTooltip : function(col, tooltip){
35782             this.config[col].tooltip = tooltip;
35783     },
35784
35785     /**
35786      * Returns the dataIndex for the specified column.
35787      * @param {Number} col The column index
35788      * @return {Number}
35789      */
35790     getDataIndex : function(col){
35791         return this.config[col].dataIndex;
35792     },
35793
35794     /**
35795      * Sets the dataIndex for a column.
35796      * @param {Number} col The column index
35797      * @param {Number} dataIndex The new dataIndex
35798      */
35799     setDataIndex : function(col, dataIndex){
35800         this.config[col].dataIndex = dataIndex;
35801     },
35802
35803     
35804     
35805     /**
35806      * Returns true if the cell is editable.
35807      * @param {Number} colIndex The column index
35808      * @param {Number} rowIndex The row index - this is nto actually used..?
35809      * @return {Boolean}
35810      */
35811     isCellEditable : function(colIndex, rowIndex){
35812         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35813     },
35814
35815     /**
35816      * Returns the editor defined for the cell/column.
35817      * return false or null to disable editing.
35818      * @param {Number} colIndex The column index
35819      * @param {Number} rowIndex The row index
35820      * @return {Object}
35821      */
35822     getCellEditor : function(colIndex, rowIndex){
35823         return this.config[colIndex].editor;
35824     },
35825
35826     /**
35827      * Sets if a column is editable.
35828      * @param {Number} col The column index
35829      * @param {Boolean} editable True if the column is editable
35830      */
35831     setEditable : function(col, editable){
35832         this.config[col].editable = editable;
35833     },
35834
35835
35836     /**
35837      * Returns true if the column is hidden.
35838      * @param {Number} colIndex The column index
35839      * @return {Boolean}
35840      */
35841     isHidden : function(colIndex){
35842         return this.config[colIndex].hidden;
35843     },
35844
35845
35846     /**
35847      * Returns true if the column width cannot be changed
35848      */
35849     isFixed : function(colIndex){
35850         return this.config[colIndex].fixed;
35851     },
35852
35853     /**
35854      * Returns true if the column can be resized
35855      * @return {Boolean}
35856      */
35857     isResizable : function(colIndex){
35858         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35859     },
35860     /**
35861      * Sets if a column is hidden.
35862      * @param {Number} colIndex The column index
35863      * @param {Boolean} hidden True if the column is hidden
35864      */
35865     setHidden : function(colIndex, hidden){
35866         this.config[colIndex].hidden = hidden;
35867         this.totalWidth = null;
35868         this.fireEvent("hiddenchange", this, colIndex, hidden);
35869     },
35870
35871     /**
35872      * Sets the editor for a column.
35873      * @param {Number} col The column index
35874      * @param {Object} editor The editor object
35875      */
35876     setEditor : function(col, editor){
35877         this.config[col].editor = editor;
35878     }
35879 });
35880
35881 Roo.grid.ColumnModel.defaultRenderer = function(value)
35882 {
35883     if(typeof value == "object") {
35884         return value;
35885     }
35886         if(typeof value == "string" && value.length < 1){
35887             return "&#160;";
35888         }
35889     
35890         return String.format("{0}", value);
35891 };
35892
35893 // Alias for backwards compatibility
35894 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35895 /*
35896  * Based on:
35897  * Ext JS Library 1.1.1
35898  * Copyright(c) 2006-2007, Ext JS, LLC.
35899  *
35900  * Originally Released Under LGPL - original licence link has changed is not relivant.
35901  *
35902  * Fork - LGPL
35903  * <script type="text/javascript">
35904  */
35905
35906 /**
35907  * @class Roo.grid.AbstractSelectionModel
35908  * @extends Roo.util.Observable
35909  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35910  * implemented by descendant classes.  This class should not be directly instantiated.
35911  * @constructor
35912  */
35913 Roo.grid.AbstractSelectionModel = function(){
35914     this.locked = false;
35915     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35916 };
35917
35918 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35919     /** @ignore Called by the grid automatically. Do not call directly. */
35920     init : function(grid){
35921         this.grid = grid;
35922         this.initEvents();
35923     },
35924
35925     /**
35926      * Locks the selections.
35927      */
35928     lock : function(){
35929         this.locked = true;
35930     },
35931
35932     /**
35933      * Unlocks the selections.
35934      */
35935     unlock : function(){
35936         this.locked = false;
35937     },
35938
35939     /**
35940      * Returns true if the selections are locked.
35941      * @return {Boolean}
35942      */
35943     isLocked : function(){
35944         return this.locked;
35945     }
35946 });/*
35947  * Based on:
35948  * Ext JS Library 1.1.1
35949  * Copyright(c) 2006-2007, Ext JS, LLC.
35950  *
35951  * Originally Released Under LGPL - original licence link has changed is not relivant.
35952  *
35953  * Fork - LGPL
35954  * <script type="text/javascript">
35955  */
35956 /**
35957  * @extends Roo.grid.AbstractSelectionModel
35958  * @class Roo.grid.RowSelectionModel
35959  * The default SelectionModel used by {@link Roo.grid.Grid}.
35960  * It supports multiple selections and keyboard selection/navigation. 
35961  * @constructor
35962  * @param {Object} config
35963  */
35964 Roo.grid.RowSelectionModel = function(config){
35965     Roo.apply(this, config);
35966     this.selections = new Roo.util.MixedCollection(false, function(o){
35967         return o.id;
35968     });
35969
35970     this.last = false;
35971     this.lastActive = false;
35972
35973     this.addEvents({
35974         /**
35975              * @event selectionchange
35976              * Fires when the selection changes
35977              * @param {SelectionModel} this
35978              */
35979             "selectionchange" : true,
35980         /**
35981              * @event afterselectionchange
35982              * Fires after the selection changes (eg. by key press or clicking)
35983              * @param {SelectionModel} this
35984              */
35985             "afterselectionchange" : true,
35986         /**
35987              * @event beforerowselect
35988              * Fires when a row is selected being selected, return false to cancel.
35989              * @param {SelectionModel} this
35990              * @param {Number} rowIndex The selected index
35991              * @param {Boolean} keepExisting False if other selections will be cleared
35992              */
35993             "beforerowselect" : true,
35994         /**
35995              * @event rowselect
35996              * Fires when a row is selected.
35997              * @param {SelectionModel} this
35998              * @param {Number} rowIndex The selected index
35999              * @param {Roo.data.Record} r The record
36000              */
36001             "rowselect" : true,
36002         /**
36003              * @event rowdeselect
36004              * Fires when a row is deselected.
36005              * @param {SelectionModel} this
36006              * @param {Number} rowIndex The selected index
36007              */
36008         "rowdeselect" : true
36009     });
36010     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
36011     this.locked = false;
36012 };
36013
36014 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
36015     /**
36016      * @cfg {Boolean} singleSelect
36017      * True to allow selection of only one row at a time (defaults to false)
36018      */
36019     singleSelect : false,
36020
36021     // private
36022     initEvents : function(){
36023
36024         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
36025             this.grid.on("mousedown", this.handleMouseDown, this);
36026         }else{ // allow click to work like normal
36027             this.grid.on("rowclick", this.handleDragableRowClick, this);
36028         }
36029
36030         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
36031             "up" : function(e){
36032                 if(!e.shiftKey){
36033                     this.selectPrevious(e.shiftKey);
36034                 }else if(this.last !== false && this.lastActive !== false){
36035                     var last = this.last;
36036                     this.selectRange(this.last,  this.lastActive-1);
36037                     this.grid.getView().focusRow(this.lastActive);
36038                     if(last !== false){
36039                         this.last = last;
36040                     }
36041                 }else{
36042                     this.selectFirstRow();
36043                 }
36044                 this.fireEvent("afterselectionchange", this);
36045             },
36046             "down" : function(e){
36047                 if(!e.shiftKey){
36048                     this.selectNext(e.shiftKey);
36049                 }else if(this.last !== false && this.lastActive !== false){
36050                     var last = this.last;
36051                     this.selectRange(this.last,  this.lastActive+1);
36052                     this.grid.getView().focusRow(this.lastActive);
36053                     if(last !== false){
36054                         this.last = last;
36055                     }
36056                 }else{
36057                     this.selectFirstRow();
36058                 }
36059                 this.fireEvent("afterselectionchange", this);
36060             },
36061             scope: this
36062         });
36063
36064         var view = this.grid.view;
36065         view.on("refresh", this.onRefresh, this);
36066         view.on("rowupdated", this.onRowUpdated, this);
36067         view.on("rowremoved", this.onRemove, this);
36068     },
36069
36070     // private
36071     onRefresh : function(){
36072         var ds = this.grid.dataSource, i, v = this.grid.view;
36073         var s = this.selections;
36074         s.each(function(r){
36075             if((i = ds.indexOfId(r.id)) != -1){
36076                 v.onRowSelect(i);
36077                 s.add(ds.getAt(i)); // updating the selection relate data
36078             }else{
36079                 s.remove(r);
36080             }
36081         });
36082     },
36083
36084     // private
36085     onRemove : function(v, index, r){
36086         this.selections.remove(r);
36087     },
36088
36089     // private
36090     onRowUpdated : function(v, index, r){
36091         if(this.isSelected(r)){
36092             v.onRowSelect(index);
36093         }
36094     },
36095
36096     /**
36097      * Select records.
36098      * @param {Array} records The records to select
36099      * @param {Boolean} keepExisting (optional) True to keep existing selections
36100      */
36101     selectRecords : function(records, keepExisting){
36102         if(!keepExisting){
36103             this.clearSelections();
36104         }
36105         var ds = this.grid.dataSource;
36106         for(var i = 0, len = records.length; i < len; i++){
36107             this.selectRow(ds.indexOf(records[i]), true);
36108         }
36109     },
36110
36111     /**
36112      * Gets the number of selected rows.
36113      * @return {Number}
36114      */
36115     getCount : function(){
36116         return this.selections.length;
36117     },
36118
36119     /**
36120      * Selects the first row in the grid.
36121      */
36122     selectFirstRow : function(){
36123         this.selectRow(0);
36124     },
36125
36126     /**
36127      * Select the last row.
36128      * @param {Boolean} keepExisting (optional) True to keep existing selections
36129      */
36130     selectLastRow : function(keepExisting){
36131         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
36132     },
36133
36134     /**
36135      * Selects the row immediately following the last selected row.
36136      * @param {Boolean} keepExisting (optional) True to keep existing selections
36137      */
36138     selectNext : function(keepExisting){
36139         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
36140             this.selectRow(this.last+1, keepExisting);
36141             this.grid.getView().focusRow(this.last);
36142         }
36143     },
36144
36145     /**
36146      * Selects the row that precedes the last selected row.
36147      * @param {Boolean} keepExisting (optional) True to keep existing selections
36148      */
36149     selectPrevious : function(keepExisting){
36150         if(this.last){
36151             this.selectRow(this.last-1, keepExisting);
36152             this.grid.getView().focusRow(this.last);
36153         }
36154     },
36155
36156     /**
36157      * Returns the selected records
36158      * @return {Array} Array of selected records
36159      */
36160     getSelections : function(){
36161         return [].concat(this.selections.items);
36162     },
36163
36164     /**
36165      * Returns the first selected record.
36166      * @return {Record}
36167      */
36168     getSelected : function(){
36169         return this.selections.itemAt(0);
36170     },
36171
36172
36173     /**
36174      * Clears all selections.
36175      */
36176     clearSelections : function(fast){
36177         if(this.locked) {
36178             return;
36179         }
36180         if(fast !== true){
36181             var ds = this.grid.dataSource;
36182             var s = this.selections;
36183             s.each(function(r){
36184                 this.deselectRow(ds.indexOfId(r.id));
36185             }, this);
36186             s.clear();
36187         }else{
36188             this.selections.clear();
36189         }
36190         this.last = false;
36191     },
36192
36193
36194     /**
36195      * Selects all rows.
36196      */
36197     selectAll : function(){
36198         if(this.locked) {
36199             return;
36200         }
36201         this.selections.clear();
36202         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
36203             this.selectRow(i, true);
36204         }
36205     },
36206
36207     /**
36208      * Returns True if there is a selection.
36209      * @return {Boolean}
36210      */
36211     hasSelection : function(){
36212         return this.selections.length > 0;
36213     },
36214
36215     /**
36216      * Returns True if the specified row is selected.
36217      * @param {Number/Record} record The record or index of the record to check
36218      * @return {Boolean}
36219      */
36220     isSelected : function(index){
36221         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
36222         return (r && this.selections.key(r.id) ? true : false);
36223     },
36224
36225     /**
36226      * Returns True if the specified record id is selected.
36227      * @param {String} id The id of record to check
36228      * @return {Boolean}
36229      */
36230     isIdSelected : function(id){
36231         return (this.selections.key(id) ? true : false);
36232     },
36233
36234     // private
36235     handleMouseDown : function(e, t){
36236         var view = this.grid.getView(), rowIndex;
36237         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
36238             return;
36239         };
36240         if(e.shiftKey && this.last !== false){
36241             var last = this.last;
36242             this.selectRange(last, rowIndex, e.ctrlKey);
36243             this.last = last; // reset the last
36244             view.focusRow(rowIndex);
36245         }else{
36246             var isSelected = this.isSelected(rowIndex);
36247             if(e.button !== 0 && isSelected){
36248                 view.focusRow(rowIndex);
36249             }else if(e.ctrlKey && isSelected){
36250                 this.deselectRow(rowIndex);
36251             }else if(!isSelected){
36252                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
36253                 view.focusRow(rowIndex);
36254             }
36255         }
36256         this.fireEvent("afterselectionchange", this);
36257     },
36258     // private
36259     handleDragableRowClick :  function(grid, rowIndex, e) 
36260     {
36261         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
36262             this.selectRow(rowIndex, false);
36263             grid.view.focusRow(rowIndex);
36264              this.fireEvent("afterselectionchange", this);
36265         }
36266     },
36267     
36268     /**
36269      * Selects multiple rows.
36270      * @param {Array} rows Array of the indexes of the row to select
36271      * @param {Boolean} keepExisting (optional) True to keep existing selections
36272      */
36273     selectRows : function(rows, keepExisting){
36274         if(!keepExisting){
36275             this.clearSelections();
36276         }
36277         for(var i = 0, len = rows.length; i < len; i++){
36278             this.selectRow(rows[i], true);
36279         }
36280     },
36281
36282     /**
36283      * Selects a range of rows. All rows in between startRow and endRow are also selected.
36284      * @param {Number} startRow The index of the first row in the range
36285      * @param {Number} endRow The index of the last row in the range
36286      * @param {Boolean} keepExisting (optional) True to retain existing selections
36287      */
36288     selectRange : function(startRow, endRow, keepExisting){
36289         if(this.locked) {
36290             return;
36291         }
36292         if(!keepExisting){
36293             this.clearSelections();
36294         }
36295         if(startRow <= endRow){
36296             for(var i = startRow; i <= endRow; i++){
36297                 this.selectRow(i, true);
36298             }
36299         }else{
36300             for(var i = startRow; i >= endRow; i--){
36301                 this.selectRow(i, true);
36302             }
36303         }
36304     },
36305
36306     /**
36307      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36308      * @param {Number} startRow The index of the first row in the range
36309      * @param {Number} endRow The index of the last row in the range
36310      */
36311     deselectRange : function(startRow, endRow, preventViewNotify){
36312         if(this.locked) {
36313             return;
36314         }
36315         for(var i = startRow; i <= endRow; i++){
36316             this.deselectRow(i, preventViewNotify);
36317         }
36318     },
36319
36320     /**
36321      * Selects a row.
36322      * @param {Number} row The index of the row to select
36323      * @param {Boolean} keepExisting (optional) True to keep existing selections
36324      */
36325     selectRow : function(index, keepExisting, preventViewNotify){
36326         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36327             return;
36328         }
36329         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36330             if(!keepExisting || this.singleSelect){
36331                 this.clearSelections();
36332             }
36333             var r = this.grid.dataSource.getAt(index);
36334             this.selections.add(r);
36335             this.last = this.lastActive = index;
36336             if(!preventViewNotify){
36337                 this.grid.getView().onRowSelect(index);
36338             }
36339             this.fireEvent("rowselect", this, index, r);
36340             this.fireEvent("selectionchange", this);
36341         }
36342     },
36343
36344     /**
36345      * Deselects a row.
36346      * @param {Number} row The index of the row to deselect
36347      */
36348     deselectRow : function(index, preventViewNotify){
36349         if(this.locked) {
36350             return;
36351         }
36352         if(this.last == index){
36353             this.last = false;
36354         }
36355         if(this.lastActive == index){
36356             this.lastActive = false;
36357         }
36358         var r = this.grid.dataSource.getAt(index);
36359         this.selections.remove(r);
36360         if(!preventViewNotify){
36361             this.grid.getView().onRowDeselect(index);
36362         }
36363         this.fireEvent("rowdeselect", this, index);
36364         this.fireEvent("selectionchange", this);
36365     },
36366
36367     // private
36368     restoreLast : function(){
36369         if(this._last){
36370             this.last = this._last;
36371         }
36372     },
36373
36374     // private
36375     acceptsNav : function(row, col, cm){
36376         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36377     },
36378
36379     // private
36380     onEditorKey : function(field, e){
36381         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36382         if(k == e.TAB){
36383             e.stopEvent();
36384             ed.completeEdit();
36385             if(e.shiftKey){
36386                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36387             }else{
36388                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36389             }
36390         }else if(k == e.ENTER && !e.ctrlKey){
36391             e.stopEvent();
36392             ed.completeEdit();
36393             if(e.shiftKey){
36394                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36395             }else{
36396                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36397             }
36398         }else if(k == e.ESC){
36399             ed.cancelEdit();
36400         }
36401         if(newCell){
36402             g.startEditing(newCell[0], newCell[1]);
36403         }
36404     }
36405 });/*
36406  * Based on:
36407  * Ext JS Library 1.1.1
36408  * Copyright(c) 2006-2007, Ext JS, LLC.
36409  *
36410  * Originally Released Under LGPL - original licence link has changed is not relivant.
36411  *
36412  * Fork - LGPL
36413  * <script type="text/javascript">
36414  */
36415 /**
36416  * @class Roo.grid.CellSelectionModel
36417  * @extends Roo.grid.AbstractSelectionModel
36418  * This class provides the basic implementation for cell selection in a grid.
36419  * @constructor
36420  * @param {Object} config The object containing the configuration of this model.
36421  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36422  */
36423 Roo.grid.CellSelectionModel = function(config){
36424     Roo.apply(this, config);
36425
36426     this.selection = null;
36427
36428     this.addEvents({
36429         /**
36430              * @event beforerowselect
36431              * Fires before a cell is selected.
36432              * @param {SelectionModel} this
36433              * @param {Number} rowIndex The selected row index
36434              * @param {Number} colIndex The selected cell index
36435              */
36436             "beforecellselect" : true,
36437         /**
36438              * @event cellselect
36439              * Fires when a cell is selected.
36440              * @param {SelectionModel} this
36441              * @param {Number} rowIndex The selected row index
36442              * @param {Number} colIndex The selected cell index
36443              */
36444             "cellselect" : true,
36445         /**
36446              * @event selectionchange
36447              * Fires when the active selection changes.
36448              * @param {SelectionModel} this
36449              * @param {Object} selection null for no selection or an object (o) with two properties
36450                 <ul>
36451                 <li>o.record: the record object for the row the selection is in</li>
36452                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36453                 </ul>
36454              */
36455             "selectionchange" : true,
36456         /**
36457              * @event tabend
36458              * Fires when the tab (or enter) was pressed on the last editable cell
36459              * You can use this to trigger add new row.
36460              * @param {SelectionModel} this
36461              */
36462             "tabend" : true,
36463          /**
36464              * @event beforeeditnext
36465              * Fires before the next editable sell is made active
36466              * You can use this to skip to another cell or fire the tabend
36467              *    if you set cell to false
36468              * @param {Object} eventdata object : { cell : [ row, col ] } 
36469              */
36470             "beforeeditnext" : true
36471     });
36472     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36473 };
36474
36475 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36476     
36477     enter_is_tab: false,
36478
36479     /** @ignore */
36480     initEvents : function(){
36481         this.grid.on("mousedown", this.handleMouseDown, this);
36482         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36483         var view = this.grid.view;
36484         view.on("refresh", this.onViewChange, this);
36485         view.on("rowupdated", this.onRowUpdated, this);
36486         view.on("beforerowremoved", this.clearSelections, this);
36487         view.on("beforerowsinserted", this.clearSelections, this);
36488         if(this.grid.isEditor){
36489             this.grid.on("beforeedit", this.beforeEdit,  this);
36490         }
36491     },
36492
36493         //private
36494     beforeEdit : function(e){
36495         this.select(e.row, e.column, false, true, e.record);
36496     },
36497
36498         //private
36499     onRowUpdated : function(v, index, r){
36500         if(this.selection && this.selection.record == r){
36501             v.onCellSelect(index, this.selection.cell[1]);
36502         }
36503     },
36504
36505         //private
36506     onViewChange : function(){
36507         this.clearSelections(true);
36508     },
36509
36510         /**
36511          * Returns the currently selected cell,.
36512          * @return {Array} The selected cell (row, column) or null if none selected.
36513          */
36514     getSelectedCell : function(){
36515         return this.selection ? this.selection.cell : null;
36516     },
36517
36518     /**
36519      * Clears all selections.
36520      * @param {Boolean} true to prevent the gridview from being notified about the change.
36521      */
36522     clearSelections : function(preventNotify){
36523         var s = this.selection;
36524         if(s){
36525             if(preventNotify !== true){
36526                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36527             }
36528             this.selection = null;
36529             this.fireEvent("selectionchange", this, null);
36530         }
36531     },
36532
36533     /**
36534      * Returns true if there is a selection.
36535      * @return {Boolean}
36536      */
36537     hasSelection : function(){
36538         return this.selection ? true : false;
36539     },
36540
36541     /** @ignore */
36542     handleMouseDown : function(e, t){
36543         var v = this.grid.getView();
36544         if(this.isLocked()){
36545             return;
36546         };
36547         var row = v.findRowIndex(t);
36548         var cell = v.findCellIndex(t);
36549         if(row !== false && cell !== false){
36550             this.select(row, cell);
36551         }
36552     },
36553
36554     /**
36555      * Selects a cell.
36556      * @param {Number} rowIndex
36557      * @param {Number} collIndex
36558      */
36559     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36560         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36561             this.clearSelections();
36562             r = r || this.grid.dataSource.getAt(rowIndex);
36563             this.selection = {
36564                 record : r,
36565                 cell : [rowIndex, colIndex]
36566             };
36567             if(!preventViewNotify){
36568                 var v = this.grid.getView();
36569                 v.onCellSelect(rowIndex, colIndex);
36570                 if(preventFocus !== true){
36571                     v.focusCell(rowIndex, colIndex);
36572                 }
36573             }
36574             this.fireEvent("cellselect", this, rowIndex, colIndex);
36575             this.fireEvent("selectionchange", this, this.selection);
36576         }
36577     },
36578
36579         //private
36580     isSelectable : function(rowIndex, colIndex, cm){
36581         return !cm.isHidden(colIndex);
36582     },
36583
36584     /** @ignore */
36585     handleKeyDown : function(e){
36586         //Roo.log('Cell Sel Model handleKeyDown');
36587         if(!e.isNavKeyPress()){
36588             return;
36589         }
36590         var g = this.grid, s = this.selection;
36591         if(!s){
36592             e.stopEvent();
36593             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36594             if(cell){
36595                 this.select(cell[0], cell[1]);
36596             }
36597             return;
36598         }
36599         var sm = this;
36600         var walk = function(row, col, step){
36601             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36602         };
36603         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36604         var newCell;
36605
36606       
36607
36608         switch(k){
36609             case e.TAB:
36610                 // handled by onEditorKey
36611                 if (g.isEditor && g.editing) {
36612                     return;
36613                 }
36614                 if(e.shiftKey) {
36615                     newCell = walk(r, c-1, -1);
36616                 } else {
36617                     newCell = walk(r, c+1, 1);
36618                 }
36619                 break;
36620             
36621             case e.DOWN:
36622                newCell = walk(r+1, c, 1);
36623                 break;
36624             
36625             case e.UP:
36626                 newCell = walk(r-1, c, -1);
36627                 break;
36628             
36629             case e.RIGHT:
36630                 newCell = walk(r, c+1, 1);
36631                 break;
36632             
36633             case e.LEFT:
36634                 newCell = walk(r, c-1, -1);
36635                 break;
36636             
36637             case e.ENTER:
36638                 
36639                 if(g.isEditor && !g.editing){
36640                    g.startEditing(r, c);
36641                    e.stopEvent();
36642                    return;
36643                 }
36644                 
36645                 
36646              break;
36647         };
36648         if(newCell){
36649             this.select(newCell[0], newCell[1]);
36650             e.stopEvent();
36651             
36652         }
36653     },
36654
36655     acceptsNav : function(row, col, cm){
36656         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36657     },
36658     /**
36659      * Selects a cell.
36660      * @param {Number} field (not used) - as it's normally used as a listener
36661      * @param {Number} e - event - fake it by using
36662      *
36663      * var e = Roo.EventObjectImpl.prototype;
36664      * e.keyCode = e.TAB
36665      *
36666      * 
36667      */
36668     onEditorKey : function(field, e){
36669         
36670         var k = e.getKey(),
36671             newCell,
36672             g = this.grid,
36673             ed = g.activeEditor,
36674             forward = false;
36675         ///Roo.log('onEditorKey' + k);
36676         
36677         
36678         if (this.enter_is_tab && k == e.ENTER) {
36679             k = e.TAB;
36680         }
36681         
36682         if(k == e.TAB){
36683             if(e.shiftKey){
36684                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36685             }else{
36686                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36687                 forward = true;
36688             }
36689             
36690             e.stopEvent();
36691             
36692         } else if(k == e.ENTER &&  !e.ctrlKey){
36693             ed.completeEdit();
36694             e.stopEvent();
36695             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36696         
36697                 } else if(k == e.ESC){
36698             ed.cancelEdit();
36699         }
36700                 
36701         if (newCell) {
36702             var ecall = { cell : newCell, forward : forward };
36703             this.fireEvent('beforeeditnext', ecall );
36704             newCell = ecall.cell;
36705                         forward = ecall.forward;
36706         }
36707                 
36708         if(newCell){
36709             //Roo.log('next cell after edit');
36710             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36711         } else if (forward) {
36712             // tabbed past last
36713             this.fireEvent.defer(100, this, ['tabend',this]);
36714         }
36715     }
36716 });/*
36717  * Based on:
36718  * Ext JS Library 1.1.1
36719  * Copyright(c) 2006-2007, Ext JS, LLC.
36720  *
36721  * Originally Released Under LGPL - original licence link has changed is not relivant.
36722  *
36723  * Fork - LGPL
36724  * <script type="text/javascript">
36725  */
36726  
36727 /**
36728  * @class Roo.grid.EditorGrid
36729  * @extends Roo.grid.Grid
36730  * Class for creating and editable grid.
36731  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36732  * The container MUST have some type of size defined for the grid to fill. The container will be 
36733  * automatically set to position relative if it isn't already.
36734  * @param {Object} dataSource The data model to bind to
36735  * @param {Object} colModel The column model with info about this grid's columns
36736  */
36737 Roo.grid.EditorGrid = function(container, config){
36738     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36739     this.getGridEl().addClass("xedit-grid");
36740
36741     if(!this.selModel){
36742         this.selModel = new Roo.grid.CellSelectionModel();
36743     }
36744
36745     this.activeEditor = null;
36746
36747         this.addEvents({
36748             /**
36749              * @event beforeedit
36750              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36751              * <ul style="padding:5px;padding-left:16px;">
36752              * <li>grid - This grid</li>
36753              * <li>record - The record being edited</li>
36754              * <li>field - The field name being edited</li>
36755              * <li>value - The value for the field being edited.</li>
36756              * <li>row - The grid row index</li>
36757              * <li>column - The grid column index</li>
36758              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36759              * </ul>
36760              * @param {Object} e An edit event (see above for description)
36761              */
36762             "beforeedit" : true,
36763             /**
36764              * @event afteredit
36765              * Fires after a cell is edited. <br />
36766              * <ul style="padding:5px;padding-left:16px;">
36767              * <li>grid - This grid</li>
36768              * <li>record - The record being edited</li>
36769              * <li>field - The field name being edited</li>
36770              * <li>value - The value being set</li>
36771              * <li>originalValue - The original value for the field, before the edit.</li>
36772              * <li>row - The grid row index</li>
36773              * <li>column - The grid column index</li>
36774              * </ul>
36775              * @param {Object} e An edit event (see above for description)
36776              */
36777             "afteredit" : true,
36778             /**
36779              * @event validateedit
36780              * Fires after a cell is edited, but before the value is set in the record. 
36781          * You can use this to modify the value being set in the field, Return false
36782              * to cancel the change. The edit event object has the following properties <br />
36783              * <ul style="padding:5px;padding-left:16px;">
36784          * <li>editor - This editor</li>
36785              * <li>grid - This grid</li>
36786              * <li>record - The record being edited</li>
36787              * <li>field - The field name being edited</li>
36788              * <li>value - The value being set</li>
36789              * <li>originalValue - The original value for the field, before the edit.</li>
36790              * <li>row - The grid row index</li>
36791              * <li>column - The grid column index</li>
36792              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36793              * </ul>
36794              * @param {Object} e An edit event (see above for description)
36795              */
36796             "validateedit" : true
36797         });
36798     this.on("bodyscroll", this.stopEditing,  this);
36799     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36800 };
36801
36802 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36803     /**
36804      * @cfg {Number} clicksToEdit
36805      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36806      */
36807     clicksToEdit: 2,
36808
36809     // private
36810     isEditor : true,
36811     // private
36812     trackMouseOver: false, // causes very odd FF errors
36813
36814     onCellDblClick : function(g, row, col){
36815         this.startEditing(row, col);
36816     },
36817
36818     onEditComplete : function(ed, value, startValue){
36819         this.editing = false;
36820         this.activeEditor = null;
36821         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36822         var r = ed.record;
36823         var field = this.colModel.getDataIndex(ed.col);
36824         var e = {
36825             grid: this,
36826             record: r,
36827             field: field,
36828             originalValue: startValue,
36829             value: value,
36830             row: ed.row,
36831             column: ed.col,
36832             cancel:false,
36833             editor: ed
36834         };
36835         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36836         cell.show();
36837           
36838         if(String(value) !== String(startValue)){
36839             
36840             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36841                 r.set(field, e.value);
36842                 // if we are dealing with a combo box..
36843                 // then we also set the 'name' colum to be the displayField
36844                 if (ed.field.displayField && ed.field.name) {
36845                     r.set(ed.field.name, ed.field.el.dom.value);
36846                 }
36847                 
36848                 delete e.cancel; //?? why!!!
36849                 this.fireEvent("afteredit", e);
36850             }
36851         } else {
36852             this.fireEvent("afteredit", e); // always fire it!
36853         }
36854         this.view.focusCell(ed.row, ed.col);
36855     },
36856
36857     /**
36858      * Starts editing the specified for the specified row/column
36859      * @param {Number} rowIndex
36860      * @param {Number} colIndex
36861      */
36862     startEditing : function(row, col){
36863         this.stopEditing();
36864         if(this.colModel.isCellEditable(col, row)){
36865             this.view.ensureVisible(row, col, true);
36866           
36867             var r = this.dataSource.getAt(row);
36868             var field = this.colModel.getDataIndex(col);
36869             var cell = Roo.get(this.view.getCell(row,col));
36870             var e = {
36871                 grid: this,
36872                 record: r,
36873                 field: field,
36874                 value: r.data[field],
36875                 row: row,
36876                 column: col,
36877                 cancel:false 
36878             };
36879             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36880                 this.editing = true;
36881                 var ed = this.colModel.getCellEditor(col, row);
36882                 
36883                 if (!ed) {
36884                     return;
36885                 }
36886                 if(!ed.rendered){
36887                     ed.render(ed.parentEl || document.body);
36888                 }
36889                 ed.field.reset();
36890                
36891                 cell.hide();
36892                 
36893                 (function(){ // complex but required for focus issues in safari, ie and opera
36894                     ed.row = row;
36895                     ed.col = col;
36896                     ed.record = r;
36897                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36898                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36899                     this.activeEditor = ed;
36900                     var v = r.data[field];
36901                     ed.startEdit(this.view.getCell(row, col), v);
36902                     // combo's with 'displayField and name set
36903                     if (ed.field.displayField && ed.field.name) {
36904                         ed.field.el.dom.value = r.data[ed.field.name];
36905                     }
36906                     
36907                     
36908                 }).defer(50, this);
36909             }
36910         }
36911     },
36912         
36913     /**
36914      * Stops any active editing
36915      */
36916     stopEditing : function(){
36917         if(this.activeEditor){
36918             this.activeEditor.completeEdit();
36919         }
36920         this.activeEditor = null;
36921     },
36922         
36923          /**
36924      * Called to get grid's drag proxy text, by default returns this.ddText.
36925      * @return {String}
36926      */
36927     getDragDropText : function(){
36928         var count = this.selModel.getSelectedCell() ? 1 : 0;
36929         return String.format(this.ddText, count, count == 1 ? '' : 's');
36930     }
36931         
36932 });/*
36933  * Based on:
36934  * Ext JS Library 1.1.1
36935  * Copyright(c) 2006-2007, Ext JS, LLC.
36936  *
36937  * Originally Released Under LGPL - original licence link has changed is not relivant.
36938  *
36939  * Fork - LGPL
36940  * <script type="text/javascript">
36941  */
36942
36943 // private - not really -- you end up using it !
36944 // This is a support class used internally by the Grid components
36945
36946 /**
36947  * @class Roo.grid.GridEditor
36948  * @extends Roo.Editor
36949  * Class for creating and editable grid elements.
36950  * @param {Object} config any settings (must include field)
36951  */
36952 Roo.grid.GridEditor = function(field, config){
36953     if (!config && field.field) {
36954         config = field;
36955         field = Roo.factory(config.field, Roo.form);
36956     }
36957     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36958     field.monitorTab = false;
36959 };
36960
36961 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36962     
36963     /**
36964      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36965      */
36966     
36967     alignment: "tl-tl",
36968     autoSize: "width",
36969     hideEl : false,
36970     cls: "x-small-editor x-grid-editor",
36971     shim:false,
36972     shadow:"frame"
36973 });/*
36974  * Based on:
36975  * Ext JS Library 1.1.1
36976  * Copyright(c) 2006-2007, Ext JS, LLC.
36977  *
36978  * Originally Released Under LGPL - original licence link has changed is not relivant.
36979  *
36980  * Fork - LGPL
36981  * <script type="text/javascript">
36982  */
36983   
36984
36985   
36986 Roo.grid.PropertyRecord = Roo.data.Record.create([
36987     {name:'name',type:'string'},  'value'
36988 ]);
36989
36990
36991 Roo.grid.PropertyStore = function(grid, source){
36992     this.grid = grid;
36993     this.store = new Roo.data.Store({
36994         recordType : Roo.grid.PropertyRecord
36995     });
36996     this.store.on('update', this.onUpdate,  this);
36997     if(source){
36998         this.setSource(source);
36999     }
37000     Roo.grid.PropertyStore.superclass.constructor.call(this);
37001 };
37002
37003
37004
37005 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
37006     setSource : function(o){
37007         this.source = o;
37008         this.store.removeAll();
37009         var data = [];
37010         for(var k in o){
37011             if(this.isEditableValue(o[k])){
37012                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
37013             }
37014         }
37015         this.store.loadRecords({records: data}, {}, true);
37016     },
37017
37018     onUpdate : function(ds, record, type){
37019         if(type == Roo.data.Record.EDIT){
37020             var v = record.data['value'];
37021             var oldValue = record.modified['value'];
37022             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
37023                 this.source[record.id] = v;
37024                 record.commit();
37025                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
37026             }else{
37027                 record.reject();
37028             }
37029         }
37030     },
37031
37032     getProperty : function(row){
37033        return this.store.getAt(row);
37034     },
37035
37036     isEditableValue: function(val){
37037         if(val && val instanceof Date){
37038             return true;
37039         }else if(typeof val == 'object' || typeof val == 'function'){
37040             return false;
37041         }
37042         return true;
37043     },
37044
37045     setValue : function(prop, value){
37046         this.source[prop] = value;
37047         this.store.getById(prop).set('value', value);
37048     },
37049
37050     getSource : function(){
37051         return this.source;
37052     }
37053 });
37054
37055 Roo.grid.PropertyColumnModel = function(grid, store){
37056     this.grid = grid;
37057     var g = Roo.grid;
37058     g.PropertyColumnModel.superclass.constructor.call(this, [
37059         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
37060         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
37061     ]);
37062     this.store = store;
37063     this.bselect = Roo.DomHelper.append(document.body, {
37064         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
37065             {tag: 'option', value: 'true', html: 'true'},
37066             {tag: 'option', value: 'false', html: 'false'}
37067         ]
37068     });
37069     Roo.id(this.bselect);
37070     var f = Roo.form;
37071     this.editors = {
37072         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
37073         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
37074         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
37075         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
37076         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
37077     };
37078     this.renderCellDelegate = this.renderCell.createDelegate(this);
37079     this.renderPropDelegate = this.renderProp.createDelegate(this);
37080 };
37081
37082 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
37083     
37084     
37085     nameText : 'Name',
37086     valueText : 'Value',
37087     
37088     dateFormat : 'm/j/Y',
37089     
37090     
37091     renderDate : function(dateVal){
37092         return dateVal.dateFormat(this.dateFormat);
37093     },
37094
37095     renderBool : function(bVal){
37096         return bVal ? 'true' : 'false';
37097     },
37098
37099     isCellEditable : function(colIndex, rowIndex){
37100         return colIndex == 1;
37101     },
37102
37103     getRenderer : function(col){
37104         return col == 1 ?
37105             this.renderCellDelegate : this.renderPropDelegate;
37106     },
37107
37108     renderProp : function(v){
37109         return this.getPropertyName(v);
37110     },
37111
37112     renderCell : function(val){
37113         var rv = val;
37114         if(val instanceof Date){
37115             rv = this.renderDate(val);
37116         }else if(typeof val == 'boolean'){
37117             rv = this.renderBool(val);
37118         }
37119         return Roo.util.Format.htmlEncode(rv);
37120     },
37121
37122     getPropertyName : function(name){
37123         var pn = this.grid.propertyNames;
37124         return pn && pn[name] ? pn[name] : name;
37125     },
37126
37127     getCellEditor : function(colIndex, rowIndex){
37128         var p = this.store.getProperty(rowIndex);
37129         var n = p.data['name'], val = p.data['value'];
37130         
37131         if(typeof(this.grid.customEditors[n]) == 'string'){
37132             return this.editors[this.grid.customEditors[n]];
37133         }
37134         if(typeof(this.grid.customEditors[n]) != 'undefined'){
37135             return this.grid.customEditors[n];
37136         }
37137         if(val instanceof Date){
37138             return this.editors['date'];
37139         }else if(typeof val == 'number'){
37140             return this.editors['number'];
37141         }else if(typeof val == 'boolean'){
37142             return this.editors['boolean'];
37143         }else{
37144             return this.editors['string'];
37145         }
37146     }
37147 });
37148
37149 /**
37150  * @class Roo.grid.PropertyGrid
37151  * @extends Roo.grid.EditorGrid
37152  * This class represents the  interface of a component based property grid control.
37153  * <br><br>Usage:<pre><code>
37154  var grid = new Roo.grid.PropertyGrid("my-container-id", {
37155       
37156  });
37157  // set any options
37158  grid.render();
37159  * </code></pre>
37160   
37161  * @constructor
37162  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37163  * The container MUST have some type of size defined for the grid to fill. The container will be
37164  * automatically set to position relative if it isn't already.
37165  * @param {Object} config A config object that sets properties on this grid.
37166  */
37167 Roo.grid.PropertyGrid = function(container, config){
37168     config = config || {};
37169     var store = new Roo.grid.PropertyStore(this);
37170     this.store = store;
37171     var cm = new Roo.grid.PropertyColumnModel(this, store);
37172     store.store.sort('name', 'ASC');
37173     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
37174         ds: store.store,
37175         cm: cm,
37176         enableColLock:false,
37177         enableColumnMove:false,
37178         stripeRows:false,
37179         trackMouseOver: false,
37180         clicksToEdit:1
37181     }, config));
37182     this.getGridEl().addClass('x-props-grid');
37183     this.lastEditRow = null;
37184     this.on('columnresize', this.onColumnResize, this);
37185     this.addEvents({
37186          /**
37187              * @event beforepropertychange
37188              * Fires before a property changes (return false to stop?)
37189              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37190              * @param {String} id Record Id
37191              * @param {String} newval New Value
37192          * @param {String} oldval Old Value
37193              */
37194         "beforepropertychange": true,
37195         /**
37196              * @event propertychange
37197              * Fires after a property changes
37198              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
37199              * @param {String} id Record Id
37200              * @param {String} newval New Value
37201          * @param {String} oldval Old Value
37202              */
37203         "propertychange": true
37204     });
37205     this.customEditors = this.customEditors || {};
37206 };
37207 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
37208     
37209      /**
37210      * @cfg {Object} customEditors map of colnames=> custom editors.
37211      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
37212      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
37213      * false disables editing of the field.
37214          */
37215     
37216       /**
37217      * @cfg {Object} propertyNames map of property Names to their displayed value
37218          */
37219     
37220     render : function(){
37221         Roo.grid.PropertyGrid.superclass.render.call(this);
37222         this.autoSize.defer(100, this);
37223     },
37224
37225     autoSize : function(){
37226         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
37227         if(this.view){
37228             this.view.fitColumns();
37229         }
37230     },
37231
37232     onColumnResize : function(){
37233         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
37234         this.autoSize();
37235     },
37236     /**
37237      * Sets the data for the Grid
37238      * accepts a Key => Value object of all the elements avaiable.
37239      * @param {Object} data  to appear in grid.
37240      */
37241     setSource : function(source){
37242         this.store.setSource(source);
37243         //this.autoSize();
37244     },
37245     /**
37246      * Gets all the data from the grid.
37247      * @return {Object} data  data stored in grid
37248      */
37249     getSource : function(){
37250         return this.store.getSource();
37251     }
37252 });/*
37253   
37254  * Licence LGPL
37255  
37256  */
37257  
37258 /**
37259  * @class Roo.grid.Calendar
37260  * @extends Roo.util.Grid
37261  * This class extends the Grid to provide a calendar widget
37262  * <br><br>Usage:<pre><code>
37263  var grid = new Roo.grid.Calendar("my-container-id", {
37264      ds: myDataStore,
37265      cm: myColModel,
37266      selModel: mySelectionModel,
37267      autoSizeColumns: true,
37268      monitorWindowResize: false,
37269      trackMouseOver: true
37270      eventstore : real data store..
37271  });
37272  // set any options
37273  grid.render();
37274   
37275   * @constructor
37276  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
37277  * The container MUST have some type of size defined for the grid to fill. The container will be
37278  * automatically set to position relative if it isn't already.
37279  * @param {Object} config A config object that sets properties on this grid.
37280  */
37281 Roo.grid.Calendar = function(container, config){
37282         // initialize the container
37283         this.container = Roo.get(container);
37284         this.container.update("");
37285         this.container.setStyle("overflow", "hidden");
37286     this.container.addClass('x-grid-container');
37287
37288     this.id = this.container.id;
37289
37290     Roo.apply(this, config);
37291     // check and correct shorthanded configs
37292     
37293     var rows = [];
37294     var d =1;
37295     for (var r = 0;r < 6;r++) {
37296         
37297         rows[r]=[];
37298         for (var c =0;c < 7;c++) {
37299             rows[r][c]= '';
37300         }
37301     }
37302     if (this.eventStore) {
37303         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37304         this.eventStore.on('load',this.onLoad, this);
37305         this.eventStore.on('beforeload',this.clearEvents, this);
37306          
37307     }
37308     
37309     this.dataSource = new Roo.data.Store({
37310             proxy: new Roo.data.MemoryProxy(rows),
37311             reader: new Roo.data.ArrayReader({}, [
37312                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37313     });
37314
37315     this.dataSource.load();
37316     this.ds = this.dataSource;
37317     this.ds.xmodule = this.xmodule || false;
37318     
37319     
37320     var cellRender = function(v,x,r)
37321     {
37322         return String.format(
37323             '<div class="fc-day  fc-widget-content"><div>' +
37324                 '<div class="fc-event-container"></div>' +
37325                 '<div class="fc-day-number">{0}</div>'+
37326                 
37327                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37328             '</div></div>', v);
37329     
37330     }
37331     
37332     
37333     this.colModel = new Roo.grid.ColumnModel( [
37334         {
37335             xtype: 'ColumnModel',
37336             xns: Roo.grid,
37337             dataIndex : 'weekday0',
37338             header : 'Sunday',
37339             renderer : cellRender
37340         },
37341         {
37342             xtype: 'ColumnModel',
37343             xns: Roo.grid,
37344             dataIndex : 'weekday1',
37345             header : 'Monday',
37346             renderer : cellRender
37347         },
37348         {
37349             xtype: 'ColumnModel',
37350             xns: Roo.grid,
37351             dataIndex : 'weekday2',
37352             header : 'Tuesday',
37353             renderer : cellRender
37354         },
37355         {
37356             xtype: 'ColumnModel',
37357             xns: Roo.grid,
37358             dataIndex : 'weekday3',
37359             header : 'Wednesday',
37360             renderer : cellRender
37361         },
37362         {
37363             xtype: 'ColumnModel',
37364             xns: Roo.grid,
37365             dataIndex : 'weekday4',
37366             header : 'Thursday',
37367             renderer : cellRender
37368         },
37369         {
37370             xtype: 'ColumnModel',
37371             xns: Roo.grid,
37372             dataIndex : 'weekday5',
37373             header : 'Friday',
37374             renderer : cellRender
37375         },
37376         {
37377             xtype: 'ColumnModel',
37378             xns: Roo.grid,
37379             dataIndex : 'weekday6',
37380             header : 'Saturday',
37381             renderer : cellRender
37382         }
37383     ]);
37384     this.cm = this.colModel;
37385     this.cm.xmodule = this.xmodule || false;
37386  
37387         
37388           
37389     //this.selModel = new Roo.grid.CellSelectionModel();
37390     //this.sm = this.selModel;
37391     //this.selModel.init(this);
37392     
37393     
37394     if(this.width){
37395         this.container.setWidth(this.width);
37396     }
37397
37398     if(this.height){
37399         this.container.setHeight(this.height);
37400     }
37401     /** @private */
37402         this.addEvents({
37403         // raw events
37404         /**
37405          * @event click
37406          * The raw click event for the entire grid.
37407          * @param {Roo.EventObject} e
37408          */
37409         "click" : true,
37410         /**
37411          * @event dblclick
37412          * The raw dblclick event for the entire grid.
37413          * @param {Roo.EventObject} e
37414          */
37415         "dblclick" : true,
37416         /**
37417          * @event contextmenu
37418          * The raw contextmenu event for the entire grid.
37419          * @param {Roo.EventObject} e
37420          */
37421         "contextmenu" : true,
37422         /**
37423          * @event mousedown
37424          * The raw mousedown event for the entire grid.
37425          * @param {Roo.EventObject} e
37426          */
37427         "mousedown" : true,
37428         /**
37429          * @event mouseup
37430          * The raw mouseup event for the entire grid.
37431          * @param {Roo.EventObject} e
37432          */
37433         "mouseup" : true,
37434         /**
37435          * @event mouseover
37436          * The raw mouseover event for the entire grid.
37437          * @param {Roo.EventObject} e
37438          */
37439         "mouseover" : true,
37440         /**
37441          * @event mouseout
37442          * The raw mouseout event for the entire grid.
37443          * @param {Roo.EventObject} e
37444          */
37445         "mouseout" : true,
37446         /**
37447          * @event keypress
37448          * The raw keypress event for the entire grid.
37449          * @param {Roo.EventObject} e
37450          */
37451         "keypress" : true,
37452         /**
37453          * @event keydown
37454          * The raw keydown event for the entire grid.
37455          * @param {Roo.EventObject} e
37456          */
37457         "keydown" : true,
37458
37459         // custom events
37460
37461         /**
37462          * @event cellclick
37463          * Fires when a cell is clicked
37464          * @param {Grid} this
37465          * @param {Number} rowIndex
37466          * @param {Number} columnIndex
37467          * @param {Roo.EventObject} e
37468          */
37469         "cellclick" : true,
37470         /**
37471          * @event celldblclick
37472          * Fires when a cell is double clicked
37473          * @param {Grid} this
37474          * @param {Number} rowIndex
37475          * @param {Number} columnIndex
37476          * @param {Roo.EventObject} e
37477          */
37478         "celldblclick" : true,
37479         /**
37480          * @event rowclick
37481          * Fires when a row is clicked
37482          * @param {Grid} this
37483          * @param {Number} rowIndex
37484          * @param {Roo.EventObject} e
37485          */
37486         "rowclick" : true,
37487         /**
37488          * @event rowdblclick
37489          * Fires when a row is double clicked
37490          * @param {Grid} this
37491          * @param {Number} rowIndex
37492          * @param {Roo.EventObject} e
37493          */
37494         "rowdblclick" : true,
37495         /**
37496          * @event headerclick
37497          * Fires when a header is clicked
37498          * @param {Grid} this
37499          * @param {Number} columnIndex
37500          * @param {Roo.EventObject} e
37501          */
37502         "headerclick" : true,
37503         /**
37504          * @event headerdblclick
37505          * Fires when a header cell is double clicked
37506          * @param {Grid} this
37507          * @param {Number} columnIndex
37508          * @param {Roo.EventObject} e
37509          */
37510         "headerdblclick" : true,
37511         /**
37512          * @event rowcontextmenu
37513          * Fires when a row is right clicked
37514          * @param {Grid} this
37515          * @param {Number} rowIndex
37516          * @param {Roo.EventObject} e
37517          */
37518         "rowcontextmenu" : true,
37519         /**
37520          * @event cellcontextmenu
37521          * Fires when a cell is right clicked
37522          * @param {Grid} this
37523          * @param {Number} rowIndex
37524          * @param {Number} cellIndex
37525          * @param {Roo.EventObject} e
37526          */
37527          "cellcontextmenu" : true,
37528         /**
37529          * @event headercontextmenu
37530          * Fires when a header is right clicked
37531          * @param {Grid} this
37532          * @param {Number} columnIndex
37533          * @param {Roo.EventObject} e
37534          */
37535         "headercontextmenu" : true,
37536         /**
37537          * @event bodyscroll
37538          * Fires when the body element is scrolled
37539          * @param {Number} scrollLeft
37540          * @param {Number} scrollTop
37541          */
37542         "bodyscroll" : true,
37543         /**
37544          * @event columnresize
37545          * Fires when the user resizes a column
37546          * @param {Number} columnIndex
37547          * @param {Number} newSize
37548          */
37549         "columnresize" : true,
37550         /**
37551          * @event columnmove
37552          * Fires when the user moves a column
37553          * @param {Number} oldIndex
37554          * @param {Number} newIndex
37555          */
37556         "columnmove" : true,
37557         /**
37558          * @event startdrag
37559          * Fires when row(s) start being dragged
37560          * @param {Grid} this
37561          * @param {Roo.GridDD} dd The drag drop object
37562          * @param {event} e The raw browser event
37563          */
37564         "startdrag" : true,
37565         /**
37566          * @event enddrag
37567          * Fires when a drag operation is complete
37568          * @param {Grid} this
37569          * @param {Roo.GridDD} dd The drag drop object
37570          * @param {event} e The raw browser event
37571          */
37572         "enddrag" : true,
37573         /**
37574          * @event dragdrop
37575          * Fires when dragged row(s) are dropped on a valid DD target
37576          * @param {Grid} this
37577          * @param {Roo.GridDD} dd The drag drop object
37578          * @param {String} targetId The target drag drop object
37579          * @param {event} e The raw browser event
37580          */
37581         "dragdrop" : true,
37582         /**
37583          * @event dragover
37584          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37585          * @param {Grid} this
37586          * @param {Roo.GridDD} dd The drag drop object
37587          * @param {String} targetId The target drag drop object
37588          * @param {event} e The raw browser event
37589          */
37590         "dragover" : true,
37591         /**
37592          * @event dragenter
37593          *  Fires when the dragged row(s) first cross another DD target while being dragged
37594          * @param {Grid} this
37595          * @param {Roo.GridDD} dd The drag drop object
37596          * @param {String} targetId The target drag drop object
37597          * @param {event} e The raw browser event
37598          */
37599         "dragenter" : true,
37600         /**
37601          * @event dragout
37602          * Fires when the dragged row(s) leave another DD target while being dragged
37603          * @param {Grid} this
37604          * @param {Roo.GridDD} dd The drag drop object
37605          * @param {String} targetId The target drag drop object
37606          * @param {event} e The raw browser event
37607          */
37608         "dragout" : true,
37609         /**
37610          * @event rowclass
37611          * Fires when a row is rendered, so you can change add a style to it.
37612          * @param {GridView} gridview   The grid view
37613          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37614          */
37615         'rowclass' : true,
37616
37617         /**
37618          * @event render
37619          * Fires when the grid is rendered
37620          * @param {Grid} grid
37621          */
37622         'render' : true,
37623             /**
37624              * @event select
37625              * Fires when a date is selected
37626              * @param {DatePicker} this
37627              * @param {Date} date The selected date
37628              */
37629         'select': true,
37630         /**
37631              * @event monthchange
37632              * Fires when the displayed month changes 
37633              * @param {DatePicker} this
37634              * @param {Date} date The selected month
37635              */
37636         'monthchange': true,
37637         /**
37638              * @event evententer
37639              * Fires when mouse over an event
37640              * @param {Calendar} this
37641              * @param {event} Event
37642              */
37643         'evententer': true,
37644         /**
37645              * @event eventleave
37646              * Fires when the mouse leaves an
37647              * @param {Calendar} this
37648              * @param {event}
37649              */
37650         'eventleave': true,
37651         /**
37652              * @event eventclick
37653              * Fires when the mouse click an
37654              * @param {Calendar} this
37655              * @param {event}
37656              */
37657         'eventclick': true,
37658         /**
37659              * @event eventrender
37660              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37661              * @param {Calendar} this
37662              * @param {data} data to be modified
37663              */
37664         'eventrender': true
37665         
37666     });
37667
37668     Roo.grid.Grid.superclass.constructor.call(this);
37669     this.on('render', function() {
37670         this.view.el.addClass('x-grid-cal'); 
37671         
37672         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37673
37674     },this);
37675     
37676     if (!Roo.grid.Calendar.style) {
37677         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37678             
37679             
37680             '.x-grid-cal .x-grid-col' :  {
37681                 height: 'auto !important',
37682                 'vertical-align': 'top'
37683             },
37684             '.x-grid-cal  .fc-event-hori' : {
37685                 height: '14px'
37686             }
37687              
37688             
37689         }, Roo.id());
37690     }
37691
37692     
37693     
37694 };
37695 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37696     /**
37697      * @cfg {Store} eventStore The store that loads events.
37698      */
37699     eventStore : 25,
37700
37701      
37702     activeDate : false,
37703     startDay : 0,
37704     autoWidth : true,
37705     monitorWindowResize : false,
37706
37707     
37708     resizeColumns : function() {
37709         var col = (this.view.el.getWidth() / 7) - 3;
37710         // loop through cols, and setWidth
37711         for(var i =0 ; i < 7 ; i++){
37712             this.cm.setColumnWidth(i, col);
37713         }
37714     },
37715      setDate :function(date) {
37716         
37717         Roo.log('setDate?');
37718         
37719         this.resizeColumns();
37720         var vd = this.activeDate;
37721         this.activeDate = date;
37722 //        if(vd && this.el){
37723 //            var t = date.getTime();
37724 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37725 //                Roo.log('using add remove');
37726 //                
37727 //                this.fireEvent('monthchange', this, date);
37728 //                
37729 //                this.cells.removeClass("fc-state-highlight");
37730 //                this.cells.each(function(c){
37731 //                   if(c.dateValue == t){
37732 //                       c.addClass("fc-state-highlight");
37733 //                       setTimeout(function(){
37734 //                            try{c.dom.firstChild.focus();}catch(e){}
37735 //                       }, 50);
37736 //                       return false;
37737 //                   }
37738 //                   return true;
37739 //                });
37740 //                return;
37741 //            }
37742 //        }
37743         
37744         var days = date.getDaysInMonth();
37745         
37746         var firstOfMonth = date.getFirstDateOfMonth();
37747         var startingPos = firstOfMonth.getDay()-this.startDay;
37748         
37749         if(startingPos < this.startDay){
37750             startingPos += 7;
37751         }
37752         
37753         var pm = date.add(Date.MONTH, -1);
37754         var prevStart = pm.getDaysInMonth()-startingPos;
37755 //        
37756         
37757         
37758         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37759         
37760         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37761         //this.cells.addClassOnOver('fc-state-hover');
37762         
37763         var cells = this.cells.elements;
37764         var textEls = this.textNodes;
37765         
37766         //Roo.each(cells, function(cell){
37767         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37768         //});
37769         
37770         days += startingPos;
37771
37772         // convert everything to numbers so it's fast
37773         var day = 86400000;
37774         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37775         //Roo.log(d);
37776         //Roo.log(pm);
37777         //Roo.log(prevStart);
37778         
37779         var today = new Date().clearTime().getTime();
37780         var sel = date.clearTime().getTime();
37781         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37782         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37783         var ddMatch = this.disabledDatesRE;
37784         var ddText = this.disabledDatesText;
37785         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37786         var ddaysText = this.disabledDaysText;
37787         var format = this.format;
37788         
37789         var setCellClass = function(cal, cell){
37790             
37791             //Roo.log('set Cell Class');
37792             cell.title = "";
37793             var t = d.getTime();
37794             
37795             //Roo.log(d);
37796             
37797             
37798             cell.dateValue = t;
37799             if(t == today){
37800                 cell.className += " fc-today";
37801                 cell.className += " fc-state-highlight";
37802                 cell.title = cal.todayText;
37803             }
37804             if(t == sel){
37805                 // disable highlight in other month..
37806                 cell.className += " fc-state-highlight";
37807                 
37808             }
37809             // disabling
37810             if(t < min) {
37811                 //cell.className = " fc-state-disabled";
37812                 cell.title = cal.minText;
37813                 return;
37814             }
37815             if(t > max) {
37816                 //cell.className = " fc-state-disabled";
37817                 cell.title = cal.maxText;
37818                 return;
37819             }
37820             if(ddays){
37821                 if(ddays.indexOf(d.getDay()) != -1){
37822                     // cell.title = ddaysText;
37823                    // cell.className = " fc-state-disabled";
37824                 }
37825             }
37826             if(ddMatch && format){
37827                 var fvalue = d.dateFormat(format);
37828                 if(ddMatch.test(fvalue)){
37829                     cell.title = ddText.replace("%0", fvalue);
37830                    cell.className = " fc-state-disabled";
37831                 }
37832             }
37833             
37834             if (!cell.initialClassName) {
37835                 cell.initialClassName = cell.dom.className;
37836             }
37837             
37838             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37839         };
37840
37841         var i = 0;
37842         
37843         for(; i < startingPos; i++) {
37844             cells[i].dayName =  (++prevStart);
37845             Roo.log(textEls[i]);
37846             d.setDate(d.getDate()+1);
37847             
37848             //cells[i].className = "fc-past fc-other-month";
37849             setCellClass(this, cells[i]);
37850         }
37851         
37852         var intDay = 0;
37853         
37854         for(; i < days; i++){
37855             intDay = i - startingPos + 1;
37856             cells[i].dayName =  (intDay);
37857             d.setDate(d.getDate()+1);
37858             
37859             cells[i].className = ''; // "x-date-active";
37860             setCellClass(this, cells[i]);
37861         }
37862         var extraDays = 0;
37863         
37864         for(; i < 42; i++) {
37865             //textEls[i].innerHTML = (++extraDays);
37866             
37867             d.setDate(d.getDate()+1);
37868             cells[i].dayName = (++extraDays);
37869             cells[i].className = "fc-future fc-other-month";
37870             setCellClass(this, cells[i]);
37871         }
37872         
37873         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37874         
37875         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37876         
37877         // this will cause all the cells to mis
37878         var rows= [];
37879         var i =0;
37880         for (var r = 0;r < 6;r++) {
37881             for (var c =0;c < 7;c++) {
37882                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37883             }    
37884         }
37885         
37886         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37887         for(i=0;i<cells.length;i++) {
37888             
37889             this.cells.elements[i].dayName = cells[i].dayName ;
37890             this.cells.elements[i].className = cells[i].className;
37891             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37892             this.cells.elements[i].title = cells[i].title ;
37893             this.cells.elements[i].dateValue = cells[i].dateValue ;
37894         }
37895         
37896         
37897         
37898         
37899         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37900         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37901         
37902         ////if(totalRows != 6){
37903             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37904            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37905        // }
37906         
37907         this.fireEvent('monthchange', this, date);
37908         
37909         
37910     },
37911  /**
37912      * Returns the grid's SelectionModel.
37913      * @return {SelectionModel}
37914      */
37915     getSelectionModel : function(){
37916         if(!this.selModel){
37917             this.selModel = new Roo.grid.CellSelectionModel();
37918         }
37919         return this.selModel;
37920     },
37921
37922     load: function() {
37923         this.eventStore.load()
37924         
37925         
37926         
37927     },
37928     
37929     findCell : function(dt) {
37930         dt = dt.clearTime().getTime();
37931         var ret = false;
37932         this.cells.each(function(c){
37933             //Roo.log("check " +c.dateValue + '?=' + dt);
37934             if(c.dateValue == dt){
37935                 ret = c;
37936                 return false;
37937             }
37938             return true;
37939         });
37940         
37941         return ret;
37942     },
37943     
37944     findCells : function(rec) {
37945         var s = rec.data.start_dt.clone().clearTime().getTime();
37946        // Roo.log(s);
37947         var e= rec.data.end_dt.clone().clearTime().getTime();
37948        // Roo.log(e);
37949         var ret = [];
37950         this.cells.each(function(c){
37951              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37952             
37953             if(c.dateValue > e){
37954                 return ;
37955             }
37956             if(c.dateValue < s){
37957                 return ;
37958             }
37959             ret.push(c);
37960         });
37961         
37962         return ret;    
37963     },
37964     
37965     findBestRow: function(cells)
37966     {
37967         var ret = 0;
37968         
37969         for (var i =0 ; i < cells.length;i++) {
37970             ret  = Math.max(cells[i].rows || 0,ret);
37971         }
37972         return ret;
37973         
37974     },
37975     
37976     
37977     addItem : function(rec)
37978     {
37979         // look for vertical location slot in
37980         var cells = this.findCells(rec);
37981         
37982         rec.row = this.findBestRow(cells);
37983         
37984         // work out the location.
37985         
37986         var crow = false;
37987         var rows = [];
37988         for(var i =0; i < cells.length; i++) {
37989             if (!crow) {
37990                 crow = {
37991                     start : cells[i],
37992                     end :  cells[i]
37993                 };
37994                 continue;
37995             }
37996             if (crow.start.getY() == cells[i].getY()) {
37997                 // on same row.
37998                 crow.end = cells[i];
37999                 continue;
38000             }
38001             // different row.
38002             rows.push(crow);
38003             crow = {
38004                 start: cells[i],
38005                 end : cells[i]
38006             };
38007             
38008         }
38009         
38010         rows.push(crow);
38011         rec.els = [];
38012         rec.rows = rows;
38013         rec.cells = cells;
38014         for (var i = 0; i < cells.length;i++) {
38015             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
38016             
38017         }
38018         
38019         
38020     },
38021     
38022     clearEvents: function() {
38023         
38024         if (!this.eventStore.getCount()) {
38025             return;
38026         }
38027         // reset number of rows in cells.
38028         Roo.each(this.cells.elements, function(c){
38029             c.rows = 0;
38030         });
38031         
38032         this.eventStore.each(function(e) {
38033             this.clearEvent(e);
38034         },this);
38035         
38036     },
38037     
38038     clearEvent : function(ev)
38039     {
38040         if (ev.els) {
38041             Roo.each(ev.els, function(el) {
38042                 el.un('mouseenter' ,this.onEventEnter, this);
38043                 el.un('mouseleave' ,this.onEventLeave, this);
38044                 el.remove();
38045             },this);
38046             ev.els = [];
38047         }
38048     },
38049     
38050     
38051     renderEvent : function(ev,ctr) {
38052         if (!ctr) {
38053              ctr = this.view.el.select('.fc-event-container',true).first();
38054         }
38055         
38056          
38057         this.clearEvent(ev);
38058             //code
38059        
38060         
38061         
38062         ev.els = [];
38063         var cells = ev.cells;
38064         var rows = ev.rows;
38065         this.fireEvent('eventrender', this, ev);
38066         
38067         for(var i =0; i < rows.length; i++) {
38068             
38069             cls = '';
38070             if (i == 0) {
38071                 cls += ' fc-event-start';
38072             }
38073             if ((i+1) == rows.length) {
38074                 cls += ' fc-event-end';
38075             }
38076             
38077             //Roo.log(ev.data);
38078             // how many rows should it span..
38079             var cg = this.eventTmpl.append(ctr,Roo.apply({
38080                 fccls : cls
38081                 
38082             }, ev.data) , true);
38083             
38084             
38085             cg.on('mouseenter' ,this.onEventEnter, this, ev);
38086             cg.on('mouseleave' ,this.onEventLeave, this, ev);
38087             cg.on('click', this.onEventClick, this, ev);
38088             
38089             ev.els.push(cg);
38090             
38091             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
38092             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
38093             //Roo.log(cg);
38094              
38095             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
38096             cg.setWidth(ebox.right - sbox.x -2);
38097         }
38098     },
38099     
38100     renderEvents: function()
38101     {   
38102         // first make sure there is enough space..
38103         
38104         if (!this.eventTmpl) {
38105             this.eventTmpl = new Roo.Template(
38106                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
38107                     '<div class="fc-event-inner">' +
38108                         '<span class="fc-event-time">{time}</span>' +
38109                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
38110                     '</div>' +
38111                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
38112                 '</div>'
38113             );
38114                 
38115         }
38116                
38117         
38118         
38119         this.cells.each(function(c) {
38120             //Roo.log(c.select('.fc-day-content div',true).first());
38121             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
38122         });
38123         
38124         var ctr = this.view.el.select('.fc-event-container',true).first();
38125         
38126         var cls;
38127         this.eventStore.each(function(ev){
38128             
38129             this.renderEvent(ev);
38130              
38131              
38132         }, this);
38133         this.view.layout();
38134         
38135     },
38136     
38137     onEventEnter: function (e, el,event,d) {
38138         this.fireEvent('evententer', this, el, event);
38139     },
38140     
38141     onEventLeave: function (e, el,event,d) {
38142         this.fireEvent('eventleave', this, el, event);
38143     },
38144     
38145     onEventClick: function (e, el,event,d) {
38146         this.fireEvent('eventclick', this, el, event);
38147     },
38148     
38149     onMonthChange: function () {
38150         this.store.load();
38151     },
38152     
38153     onLoad: function () {
38154         
38155         //Roo.log('calendar onload');
38156 //         
38157         if(this.eventStore.getCount() > 0){
38158             
38159            
38160             
38161             this.eventStore.each(function(d){
38162                 
38163                 
38164                 // FIXME..
38165                 var add =   d.data;
38166                 if (typeof(add.end_dt) == 'undefined')  {
38167                     Roo.log("Missing End time in calendar data: ");
38168                     Roo.log(d);
38169                     return;
38170                 }
38171                 if (typeof(add.start_dt) == 'undefined')  {
38172                     Roo.log("Missing Start time in calendar data: ");
38173                     Roo.log(d);
38174                     return;
38175                 }
38176                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
38177                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
38178                 add.id = add.id || d.id;
38179                 add.title = add.title || '??';
38180                 
38181                 this.addItem(d);
38182                 
38183              
38184             },this);
38185         }
38186         
38187         this.renderEvents();
38188     }
38189     
38190
38191 });
38192 /*
38193  grid : {
38194                 xtype: 'Grid',
38195                 xns: Roo.grid,
38196                 listeners : {
38197                     render : function ()
38198                     {
38199                         _this.grid = this;
38200                         
38201                         if (!this.view.el.hasClass('course-timesheet')) {
38202                             this.view.el.addClass('course-timesheet');
38203                         }
38204                         if (this.tsStyle) {
38205                             this.ds.load({});
38206                             return; 
38207                         }
38208                         Roo.log('width');
38209                         Roo.log(_this.grid.view.el.getWidth());
38210                         
38211                         
38212                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
38213                             '.course-timesheet .x-grid-row' : {
38214                                 height: '80px'
38215                             },
38216                             '.x-grid-row td' : {
38217                                 'vertical-align' : 0
38218                             },
38219                             '.course-edit-link' : {
38220                                 'color' : 'blue',
38221                                 'text-overflow' : 'ellipsis',
38222                                 'overflow' : 'hidden',
38223                                 'white-space' : 'nowrap',
38224                                 'cursor' : 'pointer'
38225                             },
38226                             '.sub-link' : {
38227                                 'color' : 'green'
38228                             },
38229                             '.de-act-sup-link' : {
38230                                 'color' : 'purple',
38231                                 'text-decoration' : 'line-through'
38232                             },
38233                             '.de-act-link' : {
38234                                 'color' : 'red',
38235                                 'text-decoration' : 'line-through'
38236                             },
38237                             '.course-timesheet .course-highlight' : {
38238                                 'border-top-style': 'dashed !important',
38239                                 'border-bottom-bottom': 'dashed !important'
38240                             },
38241                             '.course-timesheet .course-item' : {
38242                                 'font-family'   : 'tahoma, arial, helvetica',
38243                                 'font-size'     : '11px',
38244                                 'overflow'      : 'hidden',
38245                                 'padding-left'  : '10px',
38246                                 'padding-right' : '10px',
38247                                 'padding-top' : '10px' 
38248                             }
38249                             
38250                         }, Roo.id());
38251                                 this.ds.load({});
38252                     }
38253                 },
38254                 autoWidth : true,
38255                 monitorWindowResize : false,
38256                 cellrenderer : function(v,x,r)
38257                 {
38258                     return v;
38259                 },
38260                 sm : {
38261                     xtype: 'CellSelectionModel',
38262                     xns: Roo.grid
38263                 },
38264                 dataSource : {
38265                     xtype: 'Store',
38266                     xns: Roo.data,
38267                     listeners : {
38268                         beforeload : function (_self, options)
38269                         {
38270                             options.params = options.params || {};
38271                             options.params._month = _this.monthField.getValue();
38272                             options.params.limit = 9999;
38273                             options.params['sort'] = 'when_dt';    
38274                             options.params['dir'] = 'ASC';    
38275                             this.proxy.loadResponse = this.loadResponse;
38276                             Roo.log("load?");
38277                             //this.addColumns();
38278                         },
38279                         load : function (_self, records, options)
38280                         {
38281                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
38282                                 // if you click on the translation.. you can edit it...
38283                                 var el = Roo.get(this);
38284                                 var id = el.dom.getAttribute('data-id');
38285                                 var d = el.dom.getAttribute('data-date');
38286                                 var t = el.dom.getAttribute('data-time');
38287                                 //var id = this.child('span').dom.textContent;
38288                                 
38289                                 //Roo.log(this);
38290                                 Pman.Dialog.CourseCalendar.show({
38291                                     id : id,
38292                                     when_d : d,
38293                                     when_t : t,
38294                                     productitem_active : id ? 1 : 0
38295                                 }, function() {
38296                                     _this.grid.ds.load({});
38297                                 });
38298                            
38299                            });
38300                            
38301                            _this.panel.fireEvent('resize', [ '', '' ]);
38302                         }
38303                     },
38304                     loadResponse : function(o, success, response){
38305                             // this is overridden on before load..
38306                             
38307                             Roo.log("our code?");       
38308                             //Roo.log(success);
38309                             //Roo.log(response)
38310                             delete this.activeRequest;
38311                             if(!success){
38312                                 this.fireEvent("loadexception", this, o, response);
38313                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38314                                 return;
38315                             }
38316                             var result;
38317                             try {
38318                                 result = o.reader.read(response);
38319                             }catch(e){
38320                                 Roo.log("load exception?");
38321                                 this.fireEvent("loadexception", this, o, response, e);
38322                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38323                                 return;
38324                             }
38325                             Roo.log("ready...");        
38326                             // loop through result.records;
38327                             // and set this.tdate[date] = [] << array of records..
38328                             _this.tdata  = {};
38329                             Roo.each(result.records, function(r){
38330                                 //Roo.log(r.data);
38331                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38332                                     _this.tdata[r.data.when_dt.format('j')] = [];
38333                                 }
38334                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38335                             });
38336                             
38337                             //Roo.log(_this.tdata);
38338                             
38339                             result.records = [];
38340                             result.totalRecords = 6;
38341                     
38342                             // let's generate some duumy records for the rows.
38343                             //var st = _this.dateField.getValue();
38344                             
38345                             // work out monday..
38346                             //st = st.add(Date.DAY, -1 * st.format('w'));
38347                             
38348                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38349                             
38350                             var firstOfMonth = date.getFirstDayOfMonth();
38351                             var days = date.getDaysInMonth();
38352                             var d = 1;
38353                             var firstAdded = false;
38354                             for (var i = 0; i < result.totalRecords ; i++) {
38355                                 //var d= st.add(Date.DAY, i);
38356                                 var row = {};
38357                                 var added = 0;
38358                                 for(var w = 0 ; w < 7 ; w++){
38359                                     if(!firstAdded && firstOfMonth != w){
38360                                         continue;
38361                                     }
38362                                     if(d > days){
38363                                         continue;
38364                                     }
38365                                     firstAdded = true;
38366                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38367                                     row['weekday'+w] = String.format(
38368                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38369                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38370                                                     d,
38371                                                     date.format('Y-m-')+dd
38372                                                 );
38373                                     added++;
38374                                     if(typeof(_this.tdata[d]) != 'undefined'){
38375                                         Roo.each(_this.tdata[d], function(r){
38376                                             var is_sub = '';
38377                                             var deactive = '';
38378                                             var id = r.id;
38379                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38380                                             if(r.parent_id*1>0){
38381                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38382                                                 id = r.parent_id;
38383                                             }
38384                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38385                                                 deactive = 'de-act-link';
38386                                             }
38387                                             
38388                                             row['weekday'+w] += String.format(
38389                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38390                                                     id, //0
38391                                                     r.product_id_name, //1
38392                                                     r.when_dt.format('h:ia'), //2
38393                                                     is_sub, //3
38394                                                     deactive, //4
38395                                                     desc // 5
38396                                             );
38397                                         });
38398                                     }
38399                                     d++;
38400                                 }
38401                                 
38402                                 // only do this if something added..
38403                                 if(added > 0){ 
38404                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38405                                 }
38406                                 
38407                                 
38408                                 // push it twice. (second one with an hour..
38409                                 
38410                             }
38411                             //Roo.log(result);
38412                             this.fireEvent("load", this, o, o.request.arg);
38413                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38414                         },
38415                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38416                     proxy : {
38417                         xtype: 'HttpProxy',
38418                         xns: Roo.data,
38419                         method : 'GET',
38420                         url : baseURL + '/Roo/Shop_course.php'
38421                     },
38422                     reader : {
38423                         xtype: 'JsonReader',
38424                         xns: Roo.data,
38425                         id : 'id',
38426                         fields : [
38427                             {
38428                                 'name': 'id',
38429                                 'type': 'int'
38430                             },
38431                             {
38432                                 'name': 'when_dt',
38433                                 'type': 'string'
38434                             },
38435                             {
38436                                 'name': 'end_dt',
38437                                 'type': 'string'
38438                             },
38439                             {
38440                                 'name': 'parent_id',
38441                                 'type': 'int'
38442                             },
38443                             {
38444                                 'name': 'product_id',
38445                                 'type': 'int'
38446                             },
38447                             {
38448                                 'name': 'productitem_id',
38449                                 'type': 'int'
38450                             },
38451                             {
38452                                 'name': 'guid',
38453                                 'type': 'int'
38454                             }
38455                         ]
38456                     }
38457                 },
38458                 toolbar : {
38459                     xtype: 'Toolbar',
38460                     xns: Roo,
38461                     items : [
38462                         {
38463                             xtype: 'Button',
38464                             xns: Roo.Toolbar,
38465                             listeners : {
38466                                 click : function (_self, e)
38467                                 {
38468                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38469                                     sd.setMonth(sd.getMonth()-1);
38470                                     _this.monthField.setValue(sd.format('Y-m-d'));
38471                                     _this.grid.ds.load({});
38472                                 }
38473                             },
38474                             text : "Back"
38475                         },
38476                         {
38477                             xtype: 'Separator',
38478                             xns: Roo.Toolbar
38479                         },
38480                         {
38481                             xtype: 'MonthField',
38482                             xns: Roo.form,
38483                             listeners : {
38484                                 render : function (_self)
38485                                 {
38486                                     _this.monthField = _self;
38487                                    // _this.monthField.set  today
38488                                 },
38489                                 select : function (combo, date)
38490                                 {
38491                                     _this.grid.ds.load({});
38492                                 }
38493                             },
38494                             value : (function() { return new Date(); })()
38495                         },
38496                         {
38497                             xtype: 'Separator',
38498                             xns: Roo.Toolbar
38499                         },
38500                         {
38501                             xtype: 'TextItem',
38502                             xns: Roo.Toolbar,
38503                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38504                         },
38505                         {
38506                             xtype: 'Fill',
38507                             xns: Roo.Toolbar
38508                         },
38509                         {
38510                             xtype: 'Button',
38511                             xns: Roo.Toolbar,
38512                             listeners : {
38513                                 click : function (_self, e)
38514                                 {
38515                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38516                                     sd.setMonth(sd.getMonth()+1);
38517                                     _this.monthField.setValue(sd.format('Y-m-d'));
38518                                     _this.grid.ds.load({});
38519                                 }
38520                             },
38521                             text : "Next"
38522                         }
38523                     ]
38524                 },
38525                  
38526             }
38527         };
38528         
38529         *//*
38530  * Based on:
38531  * Ext JS Library 1.1.1
38532  * Copyright(c) 2006-2007, Ext JS, LLC.
38533  *
38534  * Originally Released Under LGPL - original licence link has changed is not relivant.
38535  *
38536  * Fork - LGPL
38537  * <script type="text/javascript">
38538  */
38539  
38540 /**
38541  * @class Roo.LoadMask
38542  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38543  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38544  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38545  * element's UpdateManager load indicator and will be destroyed after the initial load.
38546  * @constructor
38547  * Create a new LoadMask
38548  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38549  * @param {Object} config The config object
38550  */
38551 Roo.LoadMask = function(el, config){
38552     this.el = Roo.get(el);
38553     Roo.apply(this, config);
38554     if(this.store){
38555         this.store.on('beforeload', this.onBeforeLoad, this);
38556         this.store.on('load', this.onLoad, this);
38557         this.store.on('loadexception', this.onLoadException, this);
38558         this.removeMask = false;
38559     }else{
38560         var um = this.el.getUpdateManager();
38561         um.showLoadIndicator = false; // disable the default indicator
38562         um.on('beforeupdate', this.onBeforeLoad, this);
38563         um.on('update', this.onLoad, this);
38564         um.on('failure', this.onLoad, this);
38565         this.removeMask = true;
38566     }
38567 };
38568
38569 Roo.LoadMask.prototype = {
38570     /**
38571      * @cfg {Boolean} removeMask
38572      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38573      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38574      */
38575     /**
38576      * @cfg {String} msg
38577      * The text to display in a centered loading message box (defaults to 'Loading...')
38578      */
38579     msg : 'Loading...',
38580     /**
38581      * @cfg {String} msgCls
38582      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38583      */
38584     msgCls : 'x-mask-loading',
38585
38586     /**
38587      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38588      * @type Boolean
38589      */
38590     disabled: false,
38591
38592     /**
38593      * Disables the mask to prevent it from being displayed
38594      */
38595     disable : function(){
38596        this.disabled = true;
38597     },
38598
38599     /**
38600      * Enables the mask so that it can be displayed
38601      */
38602     enable : function(){
38603         this.disabled = false;
38604     },
38605     
38606     onLoadException : function()
38607     {
38608         Roo.log(arguments);
38609         
38610         if (typeof(arguments[3]) != 'undefined') {
38611             Roo.MessageBox.alert("Error loading",arguments[3]);
38612         } 
38613         /*
38614         try {
38615             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38616                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38617             }   
38618         } catch(e) {
38619             
38620         }
38621         */
38622     
38623         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38624     },
38625     // private
38626     onLoad : function()
38627     {
38628         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38629     },
38630
38631     // private
38632     onBeforeLoad : function(){
38633         if(!this.disabled){
38634             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38635         }
38636     },
38637
38638     // private
38639     destroy : function(){
38640         if(this.store){
38641             this.store.un('beforeload', this.onBeforeLoad, this);
38642             this.store.un('load', this.onLoad, this);
38643             this.store.un('loadexception', this.onLoadException, this);
38644         }else{
38645             var um = this.el.getUpdateManager();
38646             um.un('beforeupdate', this.onBeforeLoad, this);
38647             um.un('update', this.onLoad, this);
38648             um.un('failure', this.onLoad, this);
38649         }
38650     }
38651 };/*
38652  * Based on:
38653  * Ext JS Library 1.1.1
38654  * Copyright(c) 2006-2007, Ext JS, LLC.
38655  *
38656  * Originally Released Under LGPL - original licence link has changed is not relivant.
38657  *
38658  * Fork - LGPL
38659  * <script type="text/javascript">
38660  */
38661
38662
38663 /**
38664  * @class Roo.XTemplate
38665  * @extends Roo.Template
38666  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38667 <pre><code>
38668 var t = new Roo.XTemplate(
38669         '&lt;select name="{name}"&gt;',
38670                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38671         '&lt;/select&gt;'
38672 );
38673  
38674 // then append, applying the master template values
38675  </code></pre>
38676  *
38677  * Supported features:
38678  *
38679  *  Tags:
38680
38681 <pre><code>
38682       {a_variable} - output encoded.
38683       {a_variable.format:("Y-m-d")} - call a method on the variable
38684       {a_variable:raw} - unencoded output
38685       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38686       {a_variable:this.method_on_template(...)} - call a method on the template object.
38687  
38688 </code></pre>
38689  *  The tpl tag:
38690 <pre><code>
38691         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38692         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38693         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38694         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38695   
38696         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38697         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38698 </code></pre>
38699  *      
38700  */
38701 Roo.XTemplate = function()
38702 {
38703     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38704     if (this.html) {
38705         this.compile();
38706     }
38707 };
38708
38709
38710 Roo.extend(Roo.XTemplate, Roo.Template, {
38711
38712     /**
38713      * The various sub templates
38714      */
38715     tpls : false,
38716     /**
38717      *
38718      * basic tag replacing syntax
38719      * WORD:WORD()
38720      *
38721      * // you can fake an object call by doing this
38722      *  x.t:(test,tesT) 
38723      * 
38724      */
38725     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38726
38727     /**
38728      * compile the template
38729      *
38730      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38731      *
38732      */
38733     compile: function()
38734     {
38735         var s = this.html;
38736      
38737         s = ['<tpl>', s, '</tpl>'].join('');
38738     
38739         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38740             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38741             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38742             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38743             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38744             m,
38745             id     = 0,
38746             tpls   = [];
38747     
38748         while(true == !!(m = s.match(re))){
38749             var forMatch   = m[0].match(nameRe),
38750                 ifMatch   = m[0].match(ifRe),
38751                 execMatch   = m[0].match(execRe),
38752                 namedMatch   = m[0].match(namedRe),
38753                 
38754                 exp  = null, 
38755                 fn   = null,
38756                 exec = null,
38757                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38758                 
38759             if (ifMatch) {
38760                 // if - puts fn into test..
38761                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38762                 if(exp){
38763                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38764                 }
38765             }
38766             
38767             if (execMatch) {
38768                 // exec - calls a function... returns empty if true is  returned.
38769                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38770                 if(exp){
38771                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38772                 }
38773             }
38774             
38775             
38776             if (name) {
38777                 // for = 
38778                 switch(name){
38779                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38780                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38781                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38782                 }
38783             }
38784             var uid = namedMatch ? namedMatch[1] : id;
38785             
38786             
38787             tpls.push({
38788                 id:     namedMatch ? namedMatch[1] : id,
38789                 target: name,
38790                 exec:   exec,
38791                 test:   fn,
38792                 body:   m[1] || ''
38793             });
38794             if (namedMatch) {
38795                 s = s.replace(m[0], '');
38796             } else { 
38797                 s = s.replace(m[0], '{xtpl'+ id + '}');
38798             }
38799             ++id;
38800         }
38801         this.tpls = [];
38802         for(var i = tpls.length-1; i >= 0; --i){
38803             this.compileTpl(tpls[i]);
38804             this.tpls[tpls[i].id] = tpls[i];
38805         }
38806         this.master = tpls[tpls.length-1];
38807         return this;
38808     },
38809     /**
38810      * same as applyTemplate, except it's done to one of the subTemplates
38811      * when using named templates, you can do:
38812      *
38813      * var str = pl.applySubTemplate('your-name', values);
38814      *
38815      * 
38816      * @param {Number} id of the template
38817      * @param {Object} values to apply to template
38818      * @param {Object} parent (normaly the instance of this object)
38819      */
38820     applySubTemplate : function(id, values, parent)
38821     {
38822         
38823         
38824         var t = this.tpls[id];
38825         
38826         
38827         try { 
38828             if(t.test && !t.test.call(this, values, parent)){
38829                 return '';
38830             }
38831         } catch(e) {
38832             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38833             Roo.log(e.toString());
38834             Roo.log(t.test);
38835             return ''
38836         }
38837         try { 
38838             
38839             if(t.exec && t.exec.call(this, values, parent)){
38840                 return '';
38841             }
38842         } catch(e) {
38843             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38844             Roo.log(e.toString());
38845             Roo.log(t.exec);
38846             return ''
38847         }
38848         try {
38849             var vs = t.target ? t.target.call(this, values, parent) : values;
38850             parent = t.target ? values : parent;
38851             if(t.target && vs instanceof Array){
38852                 var buf = [];
38853                 for(var i = 0, len = vs.length; i < len; i++){
38854                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38855                 }
38856                 return buf.join('');
38857             }
38858             return t.compiled.call(this, vs, parent);
38859         } catch (e) {
38860             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38861             Roo.log(e.toString());
38862             Roo.log(t.compiled);
38863             return '';
38864         }
38865     },
38866
38867     compileTpl : function(tpl)
38868     {
38869         var fm = Roo.util.Format;
38870         var useF = this.disableFormats !== true;
38871         var sep = Roo.isGecko ? "+" : ",";
38872         var undef = function(str) {
38873             Roo.log("Property not found :"  + str);
38874             return '';
38875         };
38876         
38877         var fn = function(m, name, format, args)
38878         {
38879             //Roo.log(arguments);
38880             args = args ? args.replace(/\\'/g,"'") : args;
38881             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38882             if (typeof(format) == 'undefined') {
38883                 format= 'htmlEncode';
38884             }
38885             if (format == 'raw' ) {
38886                 format = false;
38887             }
38888             
38889             if(name.substr(0, 4) == 'xtpl'){
38890                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38891             }
38892             
38893             // build an array of options to determine if value is undefined..
38894             
38895             // basically get 'xxxx.yyyy' then do
38896             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38897             //    (function () { Roo.log("Property not found"); return ''; })() :
38898             //    ......
38899             
38900             var udef_ar = [];
38901             var lookfor = '';
38902             Roo.each(name.split('.'), function(st) {
38903                 lookfor += (lookfor.length ? '.': '') + st;
38904                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38905             });
38906             
38907             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38908             
38909             
38910             if(format && useF){
38911                 
38912                 args = args ? ',' + args : "";
38913                  
38914                 if(format.substr(0, 5) != "this."){
38915                     format = "fm." + format + '(';
38916                 }else{
38917                     format = 'this.call("'+ format.substr(5) + '", ';
38918                     args = ", values";
38919                 }
38920                 
38921                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38922             }
38923              
38924             if (args.length) {
38925                 // called with xxyx.yuu:(test,test)
38926                 // change to ()
38927                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38928             }
38929             // raw.. - :raw modifier..
38930             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38931             
38932         };
38933         var body;
38934         // branched to use + in gecko and [].join() in others
38935         if(Roo.isGecko){
38936             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38937                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38938                     "';};};";
38939         }else{
38940             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38941             body.push(tpl.body.replace(/(\r\n|\n)/g,
38942                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38943             body.push("'].join('');};};");
38944             body = body.join('');
38945         }
38946         
38947         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38948        
38949         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38950         eval(body);
38951         
38952         return this;
38953     },
38954
38955     applyTemplate : function(values){
38956         return this.master.compiled.call(this, values, {});
38957         //var s = this.subs;
38958     },
38959
38960     apply : function(){
38961         return this.applyTemplate.apply(this, arguments);
38962     }
38963
38964  });
38965
38966 Roo.XTemplate.from = function(el){
38967     el = Roo.getDom(el);
38968     return new Roo.XTemplate(el.value || el.innerHTML);
38969 };