sync
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787     
788      /**
789      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         this.loadData(this.reader.toLoadData(rec));
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @constructor
1104  * @param {Object} config
1105  */
1106 Roo.data.SimpleStore = function(config)
1107 {
1108     Roo.data.SimpleStore.superclass.constructor.call(this, {
1109         isLocal : true,
1110         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1111                 id: config.id
1112             },
1113             Roo.data.Record.create(config.fields)
1114         ),
1115         proxy : new Roo.data.MemoryProxy(config.data)
1116     });
1117     this.load();
1118 };
1119 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1120  * Based on:
1121  * Ext JS Library 1.1.1
1122  * Copyright(c) 2006-2007, Ext JS, LLC.
1123  *
1124  * Originally Released Under LGPL - original licence link has changed is not relivant.
1125  *
1126  * Fork - LGPL
1127  * <script type="text/javascript">
1128  */
1129
1130 /**
1131 /**
1132  * @extends Roo.data.Store
1133  * @class Roo.data.JsonStore
1134  * Small helper class to make creating Stores for JSON data easier. <br/>
1135 <pre><code>
1136 var store = new Roo.data.JsonStore({
1137     url: 'get-images.php',
1138     root: 'images',
1139     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1140 });
1141 </code></pre>
1142  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1143  * JsonReader and HttpProxy (unless inline data is provided).</b>
1144  * @cfg {Array} fields An array of field definition objects, or field name strings.
1145  * @constructor
1146  * @param {Object} config
1147  */
1148 Roo.data.JsonStore = function(c){
1149     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1150         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1151         reader: new Roo.data.JsonReader(c, c.fields)
1152     }));
1153 };
1154 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1155  * Based on:
1156  * Ext JS Library 1.1.1
1157  * Copyright(c) 2006-2007, Ext JS, LLC.
1158  *
1159  * Originally Released Under LGPL - original licence link has changed is not relivant.
1160  *
1161  * Fork - LGPL
1162  * <script type="text/javascript">
1163  */
1164
1165  
1166 Roo.data.Field = function(config){
1167     if(typeof config == "string"){
1168         config = {name: config};
1169     }
1170     Roo.apply(this, config);
1171     
1172     if(!this.type){
1173         this.type = "auto";
1174     }
1175     
1176     var st = Roo.data.SortTypes;
1177     // named sortTypes are supported, here we look them up
1178     if(typeof this.sortType == "string"){
1179         this.sortType = st[this.sortType];
1180     }
1181     
1182     // set default sortType for strings and dates
1183     if(!this.sortType){
1184         switch(this.type){
1185             case "string":
1186                 this.sortType = st.asUCString;
1187                 break;
1188             case "date":
1189                 this.sortType = st.asDate;
1190                 break;
1191             default:
1192                 this.sortType = st.none;
1193         }
1194     }
1195
1196     // define once
1197     var stripRe = /[\$,%]/g;
1198
1199     // prebuilt conversion function for this field, instead of
1200     // switching every time we're reading a value
1201     if(!this.convert){
1202         var cv, dateFormat = this.dateFormat;
1203         switch(this.type){
1204             case "":
1205             case "auto":
1206             case undefined:
1207                 cv = function(v){ return v; };
1208                 break;
1209             case "string":
1210                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1211                 break;
1212             case "int":
1213                 cv = function(v){
1214                     return v !== undefined && v !== null && v !== '' ?
1215                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1216                     };
1217                 break;
1218             case "float":
1219                 cv = function(v){
1220                     return v !== undefined && v !== null && v !== '' ?
1221                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1222                     };
1223                 break;
1224             case "bool":
1225             case "boolean":
1226                 cv = function(v){ return v === true || v === "true" || v == 1; };
1227                 break;
1228             case "date":
1229                 cv = function(v){
1230                     if(!v){
1231                         return '';
1232                     }
1233                     if(v instanceof Date){
1234                         return v;
1235                     }
1236                     if(dateFormat){
1237                         if(dateFormat == "timestamp"){
1238                             return new Date(v*1000);
1239                         }
1240                         return Date.parseDate(v, dateFormat);
1241                     }
1242                     var parsed = Date.parse(v);
1243                     return parsed ? new Date(parsed) : null;
1244                 };
1245              break;
1246             
1247         }
1248         this.convert = cv;
1249     }
1250 };
1251
1252 Roo.data.Field.prototype = {
1253     dateFormat: null,
1254     defaultValue: "",
1255     mapping: null,
1256     sortType : null,
1257     sortDir : "ASC"
1258 };/*
1259  * Based on:
1260  * Ext JS Library 1.1.1
1261  * Copyright(c) 2006-2007, Ext JS, LLC.
1262  *
1263  * Originally Released Under LGPL - original licence link has changed is not relivant.
1264  *
1265  * Fork - LGPL
1266  * <script type="text/javascript">
1267  */
1268  
1269 // Base class for reading structured data from a data source.  This class is intended to be
1270 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1271
1272 /**
1273  * @class Roo.data.DataReader
1274  * Base class for reading structured data from a data source.  This class is intended to be
1275  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1276  */
1277
1278 Roo.data.DataReader = function(meta, recordType){
1279     
1280     this.meta = meta;
1281     
1282     this.recordType = recordType instanceof Array ? 
1283         Roo.data.Record.create(recordType) : recordType;
1284 };
1285
1286 Roo.data.DataReader.prototype = {
1287     
1288     
1289     readerType : 'Data',
1290      /**
1291      * Create an empty record
1292      * @param {Object} data (optional) - overlay some values
1293      * @return {Roo.data.Record} record created.
1294      */
1295     newRow :  function(d) {
1296         var da =  {};
1297         this.recordType.prototype.fields.each(function(c) {
1298             switch( c.type) {
1299                 case 'int' : da[c.name] = 0; break;
1300                 case 'date' : da[c.name] = new Date(); break;
1301                 case 'float' : da[c.name] = 0.0; break;
1302                 case 'boolean' : da[c.name] = false; break;
1303                 default : da[c.name] = ""; break;
1304             }
1305             
1306         });
1307         return new this.recordType(Roo.apply(da, d));
1308     }
1309     
1310     
1311 };/*
1312  * Based on:
1313  * Ext JS Library 1.1.1
1314  * Copyright(c) 2006-2007, Ext JS, LLC.
1315  *
1316  * Originally Released Under LGPL - original licence link has changed is not relivant.
1317  *
1318  * Fork - LGPL
1319  * <script type="text/javascript">
1320  */
1321
1322 /**
1323  * @class Roo.data.DataProxy
1324  * @extends Roo.data.Observable
1325  * This class is an abstract base class for implementations which provide retrieval of
1326  * unformatted data objects.<br>
1327  * <p>
1328  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1329  * (of the appropriate type which knows how to parse the data object) to provide a block of
1330  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1331  * <p>
1332  * Custom implementations must implement the load method as described in
1333  * {@link Roo.data.HttpProxy#load}.
1334  */
1335 Roo.data.DataProxy = function(){
1336     this.addEvents({
1337         /**
1338          * @event beforeload
1339          * Fires before a network request is made to retrieve a data object.
1340          * @param {Object} This DataProxy object.
1341          * @param {Object} params The params parameter to the load function.
1342          */
1343         beforeload : true,
1344         /**
1345          * @event load
1346          * Fires before the load method's callback is called.
1347          * @param {Object} This DataProxy object.
1348          * @param {Object} o The data object.
1349          * @param {Object} arg The callback argument object passed to the load function.
1350          */
1351         load : true,
1352         /**
1353          * @event loadexception
1354          * Fires if an Exception occurs during data retrieval.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} o The data object.
1357          * @param {Object} arg The callback argument object passed to the load function.
1358          * @param {Object} e The Exception.
1359          */
1360         loadexception : true
1361     });
1362     Roo.data.DataProxy.superclass.constructor.call(this);
1363 };
1364
1365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1366
1367     /**
1368      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1369      */
1370 /*
1371  * Based on:
1372  * Ext JS Library 1.1.1
1373  * Copyright(c) 2006-2007, Ext JS, LLC.
1374  *
1375  * Originally Released Under LGPL - original licence link has changed is not relivant.
1376  *
1377  * Fork - LGPL
1378  * <script type="text/javascript">
1379  */
1380 /**
1381  * @class Roo.data.MemoryProxy
1382  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1383  * to the Reader when its load method is called.
1384  * @constructor
1385  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1386  */
1387 Roo.data.MemoryProxy = function(data){
1388     if (data.data) {
1389         data = data.data;
1390     }
1391     Roo.data.MemoryProxy.superclass.constructor.call(this);
1392     this.data = data;
1393 };
1394
1395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1396     
1397     /**
1398      * Load data from the requested source (in this case an in-memory
1399      * data object passed to the constructor), read the data object into
1400      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1401      * process that block using the passed callback.
1402      * @param {Object} params This parameter is not used by the MemoryProxy class.
1403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1404      * object into a block of Roo.data.Records.
1405      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1406      * The function must be passed <ul>
1407      * <li>The Record block object</li>
1408      * <li>The "arg" argument from the load function</li>
1409      * <li>A boolean success indicator</li>
1410      * </ul>
1411      * @param {Object} scope The scope in which to call the callback
1412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1413      */
1414     load : function(params, reader, callback, scope, arg){
1415         params = params || {};
1416         var result;
1417         try {
1418             result = reader.readRecords(params.data ? params.data :this.data);
1419         }catch(e){
1420             this.fireEvent("loadexception", this, arg, null, e);
1421             callback.call(scope, null, arg, false);
1422             return;
1423         }
1424         callback.call(scope, result, arg, true);
1425     },
1426     
1427     // private
1428     update : function(params, records){
1429         
1430     }
1431 });/*
1432  * Based on:
1433  * Ext JS Library 1.1.1
1434  * Copyright(c) 2006-2007, Ext JS, LLC.
1435  *
1436  * Originally Released Under LGPL - original licence link has changed is not relivant.
1437  *
1438  * Fork - LGPL
1439  * <script type="text/javascript">
1440  */
1441 /**
1442  * @class Roo.data.HttpProxy
1443  * @extends Roo.data.DataProxy
1444  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1445  * configured to reference a certain URL.<br><br>
1446  * <p>
1447  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1448  * from which the running page was served.<br><br>
1449  * <p>
1450  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1451  * <p>
1452  * Be aware that to enable the browser to parse an XML document, the server must set
1453  * the Content-Type header in the HTTP response to "text/xml".
1454  * @constructor
1455  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1456  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1457  * will be used to make the request.
1458  */
1459 Roo.data.HttpProxy = function(conn){
1460     Roo.data.HttpProxy.superclass.constructor.call(this);
1461     // is conn a conn config or a real conn?
1462     this.conn = conn;
1463     this.useAjax = !conn || !conn.events;
1464   
1465 };
1466
1467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1468     // thse are take from connection...
1469     
1470     /**
1471      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1472      */
1473     /**
1474      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1475      * extra parameters to each request made by this object. (defaults to undefined)
1476      */
1477     /**
1478      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1479      *  to each request made by this object. (defaults to undefined)
1480      */
1481     /**
1482      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1483      */
1484     /**
1485      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1486      */
1487      /**
1488      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1489      * @type Boolean
1490      */
1491   
1492
1493     /**
1494      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1495      * @type Boolean
1496      */
1497     /**
1498      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1499      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1500      * a finer-grained basis than the DataProxy events.
1501      */
1502     getConnection : function(){
1503         return this.useAjax ? Roo.Ajax : this.conn;
1504     },
1505
1506     /**
1507      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1508      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1509      * process that block using the passed callback.
1510      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1511      * for the request to the remote server.
1512      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1513      * object into a block of Roo.data.Records.
1514      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1515      * The function must be passed <ul>
1516      * <li>The Record block object</li>
1517      * <li>The "arg" argument from the load function</li>
1518      * <li>A boolean success indicator</li>
1519      * </ul>
1520      * @param {Object} scope The scope in which to call the callback
1521      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1522      */
1523     load : function(params, reader, callback, scope, arg){
1524         if(this.fireEvent("beforeload", this, params) !== false){
1525             var  o = {
1526                 params : params || {},
1527                 request: {
1528                     callback : callback,
1529                     scope : scope,
1530                     arg : arg
1531                 },
1532                 reader: reader,
1533                 callback : this.loadResponse,
1534                 scope: this
1535             };
1536             if(this.useAjax){
1537                 Roo.applyIf(o, this.conn);
1538                 if(this.activeRequest){
1539                     Roo.Ajax.abort(this.activeRequest);
1540                 }
1541                 this.activeRequest = Roo.Ajax.request(o);
1542             }else{
1543                 this.conn.request(o);
1544             }
1545         }else{
1546             callback.call(scope||this, null, arg, false);
1547         }
1548     },
1549
1550     // private
1551     loadResponse : function(o, success, response){
1552         delete this.activeRequest;
1553         if(!success){
1554             this.fireEvent("loadexception", this, o, response);
1555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1556             return;
1557         }
1558         var result;
1559         try {
1560             result = o.reader.read(response);
1561         }catch(e){
1562             this.fireEvent("loadexception", this, o, response, e);
1563             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1564             return;
1565         }
1566         
1567         this.fireEvent("load", this, o, o.request.arg);
1568         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1569     },
1570
1571     // private
1572     update : function(dataSet){
1573
1574     },
1575
1576     // private
1577     updateResponse : function(dataSet){
1578
1579     }
1580 });/*
1581  * Based on:
1582  * Ext JS Library 1.1.1
1583  * Copyright(c) 2006-2007, Ext JS, LLC.
1584  *
1585  * Originally Released Under LGPL - original licence link has changed is not relivant.
1586  *
1587  * Fork - LGPL
1588  * <script type="text/javascript">
1589  */
1590
1591 /**
1592  * @class Roo.data.ScriptTagProxy
1593  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1594  * other than the originating domain of the running page.<br><br>
1595  * <p>
1596  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1597  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1598  * <p>
1599  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1600  * source code that is used as the source inside a &lt;script> tag.<br><br>
1601  * <p>
1602  * In order for the browser to process the returned data, the server must wrap the data object
1603  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1604  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1605  * depending on whether the callback name was passed:
1606  * <p>
1607  * <pre><code>
1608 boolean scriptTag = false;
1609 String cb = request.getParameter("callback");
1610 if (cb != null) {
1611     scriptTag = true;
1612     response.setContentType("text/javascript");
1613 } else {
1614     response.setContentType("application/x-json");
1615 }
1616 Writer out = response.getWriter();
1617 if (scriptTag) {
1618     out.write(cb + "(");
1619 }
1620 out.print(dataBlock.toJsonString());
1621 if (scriptTag) {
1622     out.write(");");
1623 }
1624 </pre></code>
1625  *
1626  * @constructor
1627  * @param {Object} config A configuration object.
1628  */
1629 Roo.data.ScriptTagProxy = function(config){
1630     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1631     Roo.apply(this, config);
1632     this.head = document.getElementsByTagName("head")[0];
1633 };
1634
1635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1636
1637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1638     /**
1639      * @cfg {String} url The URL from which to request the data object.
1640      */
1641     /**
1642      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1643      */
1644     timeout : 30000,
1645     /**
1646      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1647      * the server the name of the callback function set up by the load call to process the returned data object.
1648      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1649      * javascript output which calls this named function passing the data object as its only parameter.
1650      */
1651     callbackParam : "callback",
1652     /**
1653      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1654      * name to the request.
1655      */
1656     nocache : true,
1657
1658     /**
1659      * Load data from the configured URL, read the data object into
1660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1661      * process that block using the passed callback.
1662      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1663      * for the request to the remote server.
1664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1665      * object into a block of Roo.data.Records.
1666      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1667      * The function must be passed <ul>
1668      * <li>The Record block object</li>
1669      * <li>The "arg" argument from the load function</li>
1670      * <li>A boolean success indicator</li>
1671      * </ul>
1672      * @param {Object} scope The scope in which to call the callback
1673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1674      */
1675     load : function(params, reader, callback, scope, arg){
1676         if(this.fireEvent("beforeload", this, params) !== false){
1677
1678             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1679
1680             var url = this.url;
1681             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1682             if(this.nocache){
1683                 url += "&_dc=" + (new Date().getTime());
1684             }
1685             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1686             var trans = {
1687                 id : transId,
1688                 cb : "stcCallback"+transId,
1689                 scriptId : "stcScript"+transId,
1690                 params : params,
1691                 arg : arg,
1692                 url : url,
1693                 callback : callback,
1694                 scope : scope,
1695                 reader : reader
1696             };
1697             var conn = this;
1698
1699             window[trans.cb] = function(o){
1700                 conn.handleResponse(o, trans);
1701             };
1702
1703             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1704
1705             if(this.autoAbort !== false){
1706                 this.abort();
1707             }
1708
1709             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1710
1711             var script = document.createElement("script");
1712             script.setAttribute("src", url);
1713             script.setAttribute("type", "text/javascript");
1714             script.setAttribute("id", trans.scriptId);
1715             this.head.appendChild(script);
1716
1717             this.trans = trans;
1718         }else{
1719             callback.call(scope||this, null, arg, false);
1720         }
1721     },
1722
1723     // private
1724     isLoading : function(){
1725         return this.trans ? true : false;
1726     },
1727
1728     /**
1729      * Abort the current server request.
1730      */
1731     abort : function(){
1732         if(this.isLoading()){
1733             this.destroyTrans(this.trans);
1734         }
1735     },
1736
1737     // private
1738     destroyTrans : function(trans, isLoaded){
1739         this.head.removeChild(document.getElementById(trans.scriptId));
1740         clearTimeout(trans.timeoutId);
1741         if(isLoaded){
1742             window[trans.cb] = undefined;
1743             try{
1744                 delete window[trans.cb];
1745             }catch(e){}
1746         }else{
1747             // if hasn't been loaded, wait for load to remove it to prevent script error
1748             window[trans.cb] = function(){
1749                 window[trans.cb] = undefined;
1750                 try{
1751                     delete window[trans.cb];
1752                 }catch(e){}
1753             };
1754         }
1755     },
1756
1757     // private
1758     handleResponse : function(o, trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, true);
1761         var result;
1762         try {
1763             result = trans.reader.readRecords(o);
1764         }catch(e){
1765             this.fireEvent("loadexception", this, o, trans.arg, e);
1766             trans.callback.call(trans.scope||window, null, trans.arg, false);
1767             return;
1768         }
1769         this.fireEvent("load", this, o, trans.arg);
1770         trans.callback.call(trans.scope||window, result, trans.arg, true);
1771     },
1772
1773     // private
1774     handleFailure : function(trans){
1775         this.trans = false;
1776         this.destroyTrans(trans, false);
1777         this.fireEvent("loadexception", this, null, trans.arg);
1778         trans.callback.call(trans.scope||window, null, trans.arg, false);
1779     }
1780 });/*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790
1791 /**
1792  * @class Roo.data.JsonReader
1793  * @extends Roo.data.DataReader
1794  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1795  * based on mappings in a provided Roo.data.Record constructor.
1796  * 
1797  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1798  * in the reply previously. 
1799  * 
1800  * <p>
1801  * Example code:
1802  * <pre><code>
1803 var RecordDef = Roo.data.Record.create([
1804     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1805     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1806 ]);
1807 var myReader = new Roo.data.JsonReader({
1808     totalProperty: "results",    // The property which contains the total dataset size (optional)
1809     root: "rows",                // The property which contains an Array of row objects
1810     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1811 }, RecordDef);
1812 </code></pre>
1813  * <p>
1814  * This would consume a JSON file like this:
1815  * <pre><code>
1816 { 'results': 2, 'rows': [
1817     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1818     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1819 }
1820 </code></pre>
1821  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1822  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1823  * paged from the remote server.
1824  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1825  * @cfg {String} root name of the property which contains the Array of row objects.
1826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1827  * @cfg {Array} fields Array of field definition objects
1828  * @constructor
1829  * Create a new JsonReader
1830  * @param {Object} meta Metadata configuration options
1831  * @param {Object} recordType Either an Array of field definition objects,
1832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1833  */
1834 Roo.data.JsonReader = function(meta, recordType){
1835     
1836     meta = meta || {};
1837     // set some defaults:
1838     Roo.applyIf(meta, {
1839         totalProperty: 'total',
1840         successProperty : 'success',
1841         root : 'data',
1842         id : 'id'
1843     });
1844     
1845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1846 };
1847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1848     
1849     readerType : 'Json',
1850     
1851     /**
1852      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1853      * Used by Store query builder to append _requestMeta to params.
1854      * 
1855      */
1856     metaFromRemote : false,
1857     /**
1858      * This method is only used by a DataProxy which has retrieved data from a remote server.
1859      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1860      * @return {Object} data A data block which is used by an Roo.data.Store object as
1861      * a cache of Roo.data.Records.
1862      */
1863     read : function(response){
1864         var json = response.responseText;
1865        
1866         var o = /* eval:var:o */ eval("("+json+")");
1867         if(!o) {
1868             throw {message: "JsonReader.read: Json object not found"};
1869         }
1870         
1871         if(o.metaData){
1872             
1873             delete this.ef;
1874             this.metaFromRemote = true;
1875             this.meta = o.metaData;
1876             this.recordType = Roo.data.Record.create(o.metaData.fields);
1877             this.onMetaChange(this.meta, this.recordType, o);
1878         }
1879         return this.readRecords(o);
1880     },
1881
1882     // private function a store will implement
1883     onMetaChange : function(meta, recordType, o){
1884
1885     },
1886
1887     /**
1888          * @ignore
1889          */
1890     simpleAccess: function(obj, subsc) {
1891         return obj[subsc];
1892     },
1893
1894         /**
1895          * @ignore
1896          */
1897     getJsonAccessor: function(){
1898         var re = /[\[\.]/;
1899         return function(expr) {
1900             try {
1901                 return(re.test(expr))
1902                     ? new Function("obj", "return obj." + expr)
1903                     : function(obj){
1904                         return obj[expr];
1905                     };
1906             } catch(e){}
1907             return Roo.emptyFn;
1908         };
1909     }(),
1910
1911     /**
1912      * Create a data block containing Roo.data.Records from an XML document.
1913      * @param {Object} o An object which contains an Array of row objects in the property specified
1914      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1915      * which contains the total size of the dataset.
1916      * @return {Object} data A data block which is used by an Roo.data.Store object as
1917      * a cache of Roo.data.Records.
1918      */
1919     readRecords : function(o){
1920         /**
1921          * After any data loads, the raw JSON data is available for further custom processing.
1922          * @type Object
1923          */
1924         this.o = o;
1925         var s = this.meta, Record = this.recordType,
1926             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1927
1928 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1929         if (!this.ef) {
1930             if(s.totalProperty) {
1931                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1932                 }
1933                 if(s.successProperty) {
1934                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1935                 }
1936                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1937                 if (s.id) {
1938                         var g = this.getJsonAccessor(s.id);
1939                         this.getId = function(rec) {
1940                                 var r = g(rec);  
1941                                 return (r === undefined || r === "") ? null : r;
1942                         };
1943                 } else {
1944                         this.getId = function(){return null;};
1945                 }
1946             this.ef = [];
1947             for(var jj = 0; jj < fl; jj++){
1948                 f = fi[jj];
1949                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1950                 this.ef[jj] = this.getJsonAccessor(map);
1951             }
1952         }
1953
1954         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1955         if(s.totalProperty){
1956             var vt = parseInt(this.getTotal(o), 10);
1957             if(!isNaN(vt)){
1958                 totalRecords = vt;
1959             }
1960         }
1961         if(s.successProperty){
1962             var vs = this.getSuccess(o);
1963             if(vs === false || vs === 'false'){
1964                 success = false;
1965             }
1966         }
1967         var records = [];
1968         for(var i = 0; i < c; i++){
1969                 var n = root[i];
1970             var values = {};
1971             var id = this.getId(n);
1972             for(var j = 0; j < fl; j++){
1973                 f = fi[j];
1974             var v = this.ef[j](n);
1975             if (!f.convert) {
1976                 Roo.log('missing convert for ' + f.name);
1977                 Roo.log(f);
1978                 continue;
1979             }
1980             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1981             }
1982             var record = new Record(values, id);
1983             record.json = n;
1984             records[i] = record;
1985         }
1986         return {
1987             raw : o,
1988             success : success,
1989             records : records,
1990             totalRecords : totalRecords
1991         };
1992     },
1993     // used when loading children.. @see loadDataFromChildren
1994     toLoadData: function(rec)
1995     {
1996         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
1997         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
1998         return { data : data, total : data.length };
1999         
2000     }
2001 });/*
2002  * Based on:
2003  * Ext JS Library 1.1.1
2004  * Copyright(c) 2006-2007, Ext JS, LLC.
2005  *
2006  * Originally Released Under LGPL - original licence link has changed is not relivant.
2007  *
2008  * Fork - LGPL
2009  * <script type="text/javascript">
2010  */
2011
2012 /**
2013  * @class Roo.data.XmlReader
2014  * @extends Roo.data.DataReader
2015  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2016  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2017  * <p>
2018  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2019  * header in the HTTP response must be set to "text/xml".</em>
2020  * <p>
2021  * Example code:
2022  * <pre><code>
2023 var RecordDef = Roo.data.Record.create([
2024    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2025    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2026 ]);
2027 var myReader = new Roo.data.XmlReader({
2028    totalRecords: "results", // The element which contains the total dataset size (optional)
2029    record: "row",           // The repeated element which contains row information
2030    id: "id"                 // The element within the row that provides an ID for the record (optional)
2031 }, RecordDef);
2032 </code></pre>
2033  * <p>
2034  * This would consume an XML file like this:
2035  * <pre><code>
2036 &lt;?xml?>
2037 &lt;dataset>
2038  &lt;results>2&lt;/results>
2039  &lt;row>
2040    &lt;id>1&lt;/id>
2041    &lt;name>Bill&lt;/name>
2042    &lt;occupation>Gardener&lt;/occupation>
2043  &lt;/row>
2044  &lt;row>
2045    &lt;id>2&lt;/id>
2046    &lt;name>Ben&lt;/name>
2047    &lt;occupation>Horticulturalist&lt;/occupation>
2048  &lt;/row>
2049 &lt;/dataset>
2050 </code></pre>
2051  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2052  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2053  * paged from the remote server.
2054  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2055  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2056  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2057  * a record identifier value.
2058  * @constructor
2059  * Create a new XmlReader
2060  * @param {Object} meta Metadata configuration options
2061  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2062  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2063  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2064  */
2065 Roo.data.XmlReader = function(meta, recordType){
2066     meta = meta || {};
2067     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2068 };
2069 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2070     
2071     readerType : 'Xml',
2072     
2073     /**
2074      * This method is only used by a DataProxy which has retrieved data from a remote server.
2075          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2076          * to contain a method called 'responseXML' that returns an XML document object.
2077      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2078      * a cache of Roo.data.Records.
2079      */
2080     read : function(response){
2081         var doc = response.responseXML;
2082         if(!doc) {
2083             throw {message: "XmlReader.read: XML Document not available"};
2084         }
2085         return this.readRecords(doc);
2086     },
2087
2088     /**
2089      * Create a data block containing Roo.data.Records from an XML document.
2090          * @param {Object} doc A parsed XML document.
2091      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2092      * a cache of Roo.data.Records.
2093      */
2094     readRecords : function(doc){
2095         /**
2096          * After any data loads/reads, the raw XML Document is available for further custom processing.
2097          * @type XMLDocument
2098          */
2099         this.xmlData = doc;
2100         var root = doc.documentElement || doc;
2101         var q = Roo.DomQuery;
2102         var recordType = this.recordType, fields = recordType.prototype.fields;
2103         var sid = this.meta.id;
2104         var totalRecords = 0, success = true;
2105         if(this.meta.totalRecords){
2106             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2107         }
2108         
2109         if(this.meta.success){
2110             var sv = q.selectValue(this.meta.success, root, true);
2111             success = sv !== false && sv !== 'false';
2112         }
2113         var records = [];
2114         var ns = q.select(this.meta.record, root);
2115         for(var i = 0, len = ns.length; i < len; i++) {
2116                 var n = ns[i];
2117                 var values = {};
2118                 var id = sid ? q.selectValue(sid, n) : undefined;
2119                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2120                     var f = fields.items[j];
2121                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2122                     v = f.convert(v);
2123                     values[f.name] = v;
2124                 }
2125                 var record = new recordType(values, id);
2126                 record.node = n;
2127                 records[records.length] = record;
2128             }
2129
2130             return {
2131                 success : success,
2132                 records : records,
2133                 totalRecords : totalRecords || records.length
2134             };
2135     }
2136 });/*
2137  * Based on:
2138  * Ext JS Library 1.1.1
2139  * Copyright(c) 2006-2007, Ext JS, LLC.
2140  *
2141  * Originally Released Under LGPL - original licence link has changed is not relivant.
2142  *
2143  * Fork - LGPL
2144  * <script type="text/javascript">
2145  */
2146
2147 /**
2148  * @class Roo.data.ArrayReader
2149  * @extends Roo.data.DataReader
2150  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2151  * Each element of that Array represents a row of data fields. The
2152  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2153  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2154  * <p>
2155  * Example code:.
2156  * <pre><code>
2157 var RecordDef = Roo.data.Record.create([
2158     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2159     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2160 ]);
2161 var myReader = new Roo.data.ArrayReader({
2162     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2163 }, RecordDef);
2164 </code></pre>
2165  * <p>
2166  * This would consume an Array like this:
2167  * <pre><code>
2168 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2169   </code></pre>
2170  
2171  * @constructor
2172  * Create a new JsonReader
2173  * @param {Object} meta Metadata configuration options.
2174  * @param {Object|Array} recordType Either an Array of field definition objects
2175  * 
2176  * @cfg {Array} fields Array of field definition objects
2177  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2178  * as specified to {@link Roo.data.Record#create},
2179  * or an {@link Roo.data.Record} object
2180  *
2181  * 
2182  * created using {@link Roo.data.Record#create}.
2183  */
2184 Roo.data.ArrayReader = function(meta, recordType)
2185 {    
2186     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2187 };
2188
2189 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2190     
2191       /**
2192      * Create a data block containing Roo.data.Records from an XML document.
2193      * @param {Object} o An Array of row objects which represents the dataset.
2194      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2195      * a cache of Roo.data.Records.
2196      */
2197     readRecords : function(o)
2198     {
2199         var sid = this.meta ? this.meta.id : null;
2200         var recordType = this.recordType, fields = recordType.prototype.fields;
2201         var records = [];
2202         var root = o;
2203         for(var i = 0; i < root.length; i++){
2204                 var n = root[i];
2205             var values = {};
2206             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2207             for(var j = 0, jlen = fields.length; j < jlen; j++){
2208                 var f = fields.items[j];
2209                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2210                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2211                 v = f.convert(v);
2212                 values[f.name] = v;
2213             }
2214             var record = new recordType(values, id);
2215             record.json = n;
2216             records[records.length] = record;
2217         }
2218         return {
2219             records : records,
2220             totalRecords : records.length
2221         };
2222     },
2223     // used when loading children.. @see loadDataFromChildren
2224     toLoadData: function(rec)
2225     {
2226         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2227         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2228         
2229     }
2230     
2231     
2232 });/*
2233  * Based on:
2234  * Ext JS Library 1.1.1
2235  * Copyright(c) 2006-2007, Ext JS, LLC.
2236  *
2237  * Originally Released Under LGPL - original licence link has changed is not relivant.
2238  *
2239  * Fork - LGPL
2240  * <script type="text/javascript">
2241  */
2242
2243
2244 /**
2245  * @class Roo.data.Tree
2246  * @extends Roo.util.Observable
2247  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2248  * in the tree have most standard DOM functionality.
2249  * @constructor
2250  * @param {Node} root (optional) The root node
2251  */
2252 Roo.data.Tree = function(root){
2253    this.nodeHash = {};
2254    /**
2255     * The root node for this tree
2256     * @type Node
2257     */
2258    this.root = null;
2259    if(root){
2260        this.setRootNode(root);
2261    }
2262    this.addEvents({
2263        /**
2264         * @event append
2265         * Fires when a new child node is appended to a node in this tree.
2266         * @param {Tree} tree The owner tree
2267         * @param {Node} parent The parent node
2268         * @param {Node} node The newly appended node
2269         * @param {Number} index The index of the newly appended node
2270         */
2271        "append" : true,
2272        /**
2273         * @event remove
2274         * Fires when a child node is removed from a node in this tree.
2275         * @param {Tree} tree The owner tree
2276         * @param {Node} parent The parent node
2277         * @param {Node} node The child node removed
2278         */
2279        "remove" : true,
2280        /**
2281         * @event move
2282         * Fires when a node is moved to a new location in the tree
2283         * @param {Tree} tree The owner tree
2284         * @param {Node} node The node moved
2285         * @param {Node} oldParent The old parent of this node
2286         * @param {Node} newParent The new parent of this node
2287         * @param {Number} index The index it was moved to
2288         */
2289        "move" : true,
2290        /**
2291         * @event insert
2292         * Fires when a new child node is inserted in a node in this tree.
2293         * @param {Tree} tree The owner tree
2294         * @param {Node} parent The parent node
2295         * @param {Node} node The child node inserted
2296         * @param {Node} refNode The child node the node was inserted before
2297         */
2298        "insert" : true,
2299        /**
2300         * @event beforeappend
2301         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2302         * @param {Tree} tree The owner tree
2303         * @param {Node} parent The parent node
2304         * @param {Node} node The child node to be appended
2305         */
2306        "beforeappend" : true,
2307        /**
2308         * @event beforeremove
2309         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2310         * @param {Tree} tree The owner tree
2311         * @param {Node} parent The parent node
2312         * @param {Node} node The child node to be removed
2313         */
2314        "beforeremove" : true,
2315        /**
2316         * @event beforemove
2317         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2318         * @param {Tree} tree The owner tree
2319         * @param {Node} node The node being moved
2320         * @param {Node} oldParent The parent of the node
2321         * @param {Node} newParent The new parent the node is moving to
2322         * @param {Number} index The index it is being moved to
2323         */
2324        "beforemove" : true,
2325        /**
2326         * @event beforeinsert
2327         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2328         * @param {Tree} tree The owner tree
2329         * @param {Node} parent The parent node
2330         * @param {Node} node The child node to be inserted
2331         * @param {Node} refNode The child node the node is being inserted before
2332         */
2333        "beforeinsert" : true
2334    });
2335
2336     Roo.data.Tree.superclass.constructor.call(this);
2337 };
2338
2339 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2340     pathSeparator: "/",
2341
2342     proxyNodeEvent : function(){
2343         return this.fireEvent.apply(this, arguments);
2344     },
2345
2346     /**
2347      * Returns the root node for this tree.
2348      * @return {Node}
2349      */
2350     getRootNode : function(){
2351         return this.root;
2352     },
2353
2354     /**
2355      * Sets the root node for this tree.
2356      * @param {Node} node
2357      * @return {Node}
2358      */
2359     setRootNode : function(node){
2360         this.root = node;
2361         node.ownerTree = this;
2362         node.isRoot = true;
2363         this.registerNode(node);
2364         return node;
2365     },
2366
2367     /**
2368      * Gets a node in this tree by its id.
2369      * @param {String} id
2370      * @return {Node}
2371      */
2372     getNodeById : function(id){
2373         return this.nodeHash[id];
2374     },
2375
2376     registerNode : function(node){
2377         this.nodeHash[node.id] = node;
2378     },
2379
2380     unregisterNode : function(node){
2381         delete this.nodeHash[node.id];
2382     },
2383
2384     toString : function(){
2385         return "[Tree"+(this.id?" "+this.id:"")+"]";
2386     }
2387 });
2388
2389 /**
2390  * @class Roo.data.Node
2391  * @extends Roo.util.Observable
2392  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2393  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2394  * @constructor
2395  * @param {Object} attributes The attributes/config for the node
2396  */
2397 Roo.data.Node = function(attributes){
2398     /**
2399      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2400      * @type {Object}
2401      */
2402     this.attributes = attributes || {};
2403     this.leaf = this.attributes.leaf;
2404     /**
2405      * The node id. @type String
2406      */
2407     this.id = this.attributes.id;
2408     if(!this.id){
2409         this.id = Roo.id(null, "ynode-");
2410         this.attributes.id = this.id;
2411     }
2412      
2413     
2414     /**
2415      * All child nodes of this node. @type Array
2416      */
2417     this.childNodes = [];
2418     if(!this.childNodes.indexOf){ // indexOf is a must
2419         this.childNodes.indexOf = function(o){
2420             for(var i = 0, len = this.length; i < len; i++){
2421                 if(this[i] == o) {
2422                     return i;
2423                 }
2424             }
2425             return -1;
2426         };
2427     }
2428     /**
2429      * The parent node for this node. @type Node
2430      */
2431     this.parentNode = null;
2432     /**
2433      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2434      */
2435     this.firstChild = null;
2436     /**
2437      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2438      */
2439     this.lastChild = null;
2440     /**
2441      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2442      */
2443     this.previousSibling = null;
2444     /**
2445      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2446      */
2447     this.nextSibling = null;
2448
2449     this.addEvents({
2450        /**
2451         * @event append
2452         * Fires when a new child node is appended
2453         * @param {Tree} tree The owner tree
2454         * @param {Node} this This node
2455         * @param {Node} node The newly appended node
2456         * @param {Number} index The index of the newly appended node
2457         */
2458        "append" : true,
2459        /**
2460         * @event remove
2461         * Fires when a child node is removed
2462         * @param {Tree} tree The owner tree
2463         * @param {Node} this This node
2464         * @param {Node} node The removed node
2465         */
2466        "remove" : true,
2467        /**
2468         * @event move
2469         * Fires when this node is moved to a new location in the tree
2470         * @param {Tree} tree The owner tree
2471         * @param {Node} this This node
2472         * @param {Node} oldParent The old parent of this node
2473         * @param {Node} newParent The new parent of this node
2474         * @param {Number} index The index it was moved to
2475         */
2476        "move" : true,
2477        /**
2478         * @event insert
2479         * Fires when a new child node is inserted.
2480         * @param {Tree} tree The owner tree
2481         * @param {Node} this This node
2482         * @param {Node} node The child node inserted
2483         * @param {Node} refNode The child node the node was inserted before
2484         */
2485        "insert" : true,
2486        /**
2487         * @event beforeappend
2488         * Fires before a new child is appended, return false to cancel the append.
2489         * @param {Tree} tree The owner tree
2490         * @param {Node} this This node
2491         * @param {Node} node The child node to be appended
2492         */
2493        "beforeappend" : true,
2494        /**
2495         * @event beforeremove
2496         * Fires before a child is removed, return false to cancel the remove.
2497         * @param {Tree} tree The owner tree
2498         * @param {Node} this This node
2499         * @param {Node} node The child node to be removed
2500         */
2501        "beforeremove" : true,
2502        /**
2503         * @event beforemove
2504         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2505         * @param {Tree} tree The owner tree
2506         * @param {Node} this This node
2507         * @param {Node} oldParent The parent of this node
2508         * @param {Node} newParent The new parent this node is moving to
2509         * @param {Number} index The index it is being moved to
2510         */
2511        "beforemove" : true,
2512        /**
2513         * @event beforeinsert
2514         * Fires before a new child is inserted, return false to cancel the insert.
2515         * @param {Tree} tree The owner tree
2516         * @param {Node} this This node
2517         * @param {Node} node The child node to be inserted
2518         * @param {Node} refNode The child node the node is being inserted before
2519         */
2520        "beforeinsert" : true
2521    });
2522     this.listeners = this.attributes.listeners;
2523     Roo.data.Node.superclass.constructor.call(this);
2524 };
2525
2526 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2527     fireEvent : function(evtName){
2528         // first do standard event for this node
2529         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2530             return false;
2531         }
2532         // then bubble it up to the tree if the event wasn't cancelled
2533         var ot = this.getOwnerTree();
2534         if(ot){
2535             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2536                 return false;
2537             }
2538         }
2539         return true;
2540     },
2541
2542     /**
2543      * Returns true if this node is a leaf
2544      * @return {Boolean}
2545      */
2546     isLeaf : function(){
2547         return this.leaf === true;
2548     },
2549
2550     // private
2551     setFirstChild : function(node){
2552         this.firstChild = node;
2553     },
2554
2555     //private
2556     setLastChild : function(node){
2557         this.lastChild = node;
2558     },
2559
2560
2561     /**
2562      * Returns true if this node is the last child of its parent
2563      * @return {Boolean}
2564      */
2565     isLast : function(){
2566        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2567     },
2568
2569     /**
2570      * Returns true if this node is the first child of its parent
2571      * @return {Boolean}
2572      */
2573     isFirst : function(){
2574        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2575     },
2576
2577     hasChildNodes : function(){
2578         return !this.isLeaf() && this.childNodes.length > 0;
2579     },
2580
2581     /**
2582      * Insert node(s) as the last child node of this node.
2583      * @param {Node/Array} node The node or Array of nodes to append
2584      * @return {Node} The appended node if single append, or null if an array was passed
2585      */
2586     appendChild : function(node){
2587         var multi = false;
2588         if(node instanceof Array){
2589             multi = node;
2590         }else if(arguments.length > 1){
2591             multi = arguments;
2592         }
2593         
2594         // if passed an array or multiple args do them one by one
2595         if(multi){
2596             for(var i = 0, len = multi.length; i < len; i++) {
2597                 this.appendChild(multi[i]);
2598             }
2599         }else{
2600             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2601                 return false;
2602             }
2603             var index = this.childNodes.length;
2604             var oldParent = node.parentNode;
2605             // it's a move, make sure we move it cleanly
2606             if(oldParent){
2607                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2608                     return false;
2609                 }
2610                 oldParent.removeChild(node);
2611             }
2612             
2613             index = this.childNodes.length;
2614             if(index == 0){
2615                 this.setFirstChild(node);
2616             }
2617             this.childNodes.push(node);
2618             node.parentNode = this;
2619             var ps = this.childNodes[index-1];
2620             if(ps){
2621                 node.previousSibling = ps;
2622                 ps.nextSibling = node;
2623             }else{
2624                 node.previousSibling = null;
2625             }
2626             node.nextSibling = null;
2627             this.setLastChild(node);
2628             node.setOwnerTree(this.getOwnerTree());
2629             this.fireEvent("append", this.ownerTree, this, node, index);
2630             if(this.ownerTree) {
2631                 this.ownerTree.fireEvent("appendnode", this, node, index);
2632             }
2633             if(oldParent){
2634                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2635             }
2636             return node;
2637         }
2638     },
2639
2640     /**
2641      * Removes a child node from this node.
2642      * @param {Node} node The node to remove
2643      * @return {Node} The removed node
2644      */
2645     removeChild : function(node){
2646         var index = this.childNodes.indexOf(node);
2647         if(index == -1){
2648             return false;
2649         }
2650         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2651             return false;
2652         }
2653
2654         // remove it from childNodes collection
2655         this.childNodes.splice(index, 1);
2656
2657         // update siblings
2658         if(node.previousSibling){
2659             node.previousSibling.nextSibling = node.nextSibling;
2660         }
2661         if(node.nextSibling){
2662             node.nextSibling.previousSibling = node.previousSibling;
2663         }
2664
2665         // update child refs
2666         if(this.firstChild == node){
2667             this.setFirstChild(node.nextSibling);
2668         }
2669         if(this.lastChild == node){
2670             this.setLastChild(node.previousSibling);
2671         }
2672
2673         node.setOwnerTree(null);
2674         // clear any references from the node
2675         node.parentNode = null;
2676         node.previousSibling = null;
2677         node.nextSibling = null;
2678         this.fireEvent("remove", this.ownerTree, this, node);
2679         return node;
2680     },
2681
2682     /**
2683      * Inserts the first node before the second node in this nodes childNodes collection.
2684      * @param {Node} node The node to insert
2685      * @param {Node} refNode The node to insert before (if null the node is appended)
2686      * @return {Node} The inserted node
2687      */
2688     insertBefore : function(node, refNode){
2689         if(!refNode){ // like standard Dom, refNode can be null for append
2690             return this.appendChild(node);
2691         }
2692         // nothing to do
2693         if(node == refNode){
2694             return false;
2695         }
2696
2697         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2698             return false;
2699         }
2700         var index = this.childNodes.indexOf(refNode);
2701         var oldParent = node.parentNode;
2702         var refIndex = index;
2703
2704         // when moving internally, indexes will change after remove
2705         if(oldParent == this && this.childNodes.indexOf(node) < index){
2706             refIndex--;
2707         }
2708
2709         // it's a move, make sure we move it cleanly
2710         if(oldParent){
2711             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2712                 return false;
2713             }
2714             oldParent.removeChild(node);
2715         }
2716         if(refIndex == 0){
2717             this.setFirstChild(node);
2718         }
2719         this.childNodes.splice(refIndex, 0, node);
2720         node.parentNode = this;
2721         var ps = this.childNodes[refIndex-1];
2722         if(ps){
2723             node.previousSibling = ps;
2724             ps.nextSibling = node;
2725         }else{
2726             node.previousSibling = null;
2727         }
2728         node.nextSibling = refNode;
2729         refNode.previousSibling = node;
2730         node.setOwnerTree(this.getOwnerTree());
2731         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2732         if(oldParent){
2733             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2734         }
2735         return node;
2736     },
2737
2738     /**
2739      * Returns the child node at the specified index.
2740      * @param {Number} index
2741      * @return {Node}
2742      */
2743     item : function(index){
2744         return this.childNodes[index];
2745     },
2746
2747     /**
2748      * Replaces one child node in this node with another.
2749      * @param {Node} newChild The replacement node
2750      * @param {Node} oldChild The node to replace
2751      * @return {Node} The replaced node
2752      */
2753     replaceChild : function(newChild, oldChild){
2754         this.insertBefore(newChild, oldChild);
2755         this.removeChild(oldChild);
2756         return oldChild;
2757     },
2758
2759     /**
2760      * Returns the index of a child node
2761      * @param {Node} node
2762      * @return {Number} The index of the node or -1 if it was not found
2763      */
2764     indexOf : function(child){
2765         return this.childNodes.indexOf(child);
2766     },
2767
2768     /**
2769      * Returns the tree this node is in.
2770      * @return {Tree}
2771      */
2772     getOwnerTree : function(){
2773         // if it doesn't have one, look for one
2774         if(!this.ownerTree){
2775             var p = this;
2776             while(p){
2777                 if(p.ownerTree){
2778                     this.ownerTree = p.ownerTree;
2779                     break;
2780                 }
2781                 p = p.parentNode;
2782             }
2783         }
2784         return this.ownerTree;
2785     },
2786
2787     /**
2788      * Returns depth of this node (the root node has a depth of 0)
2789      * @return {Number}
2790      */
2791     getDepth : function(){
2792         var depth = 0;
2793         var p = this;
2794         while(p.parentNode){
2795             ++depth;
2796             p = p.parentNode;
2797         }
2798         return depth;
2799     },
2800
2801     // private
2802     setOwnerTree : function(tree){
2803         // if it's move, we need to update everyone
2804         if(tree != this.ownerTree){
2805             if(this.ownerTree){
2806                 this.ownerTree.unregisterNode(this);
2807             }
2808             this.ownerTree = tree;
2809             var cs = this.childNodes;
2810             for(var i = 0, len = cs.length; i < len; i++) {
2811                 cs[i].setOwnerTree(tree);
2812             }
2813             if(tree){
2814                 tree.registerNode(this);
2815             }
2816         }
2817     },
2818
2819     /**
2820      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2821      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2822      * @return {String} The path
2823      */
2824     getPath : function(attr){
2825         attr = attr || "id";
2826         var p = this.parentNode;
2827         var b = [this.attributes[attr]];
2828         while(p){
2829             b.unshift(p.attributes[attr]);
2830             p = p.parentNode;
2831         }
2832         var sep = this.getOwnerTree().pathSeparator;
2833         return sep + b.join(sep);
2834     },
2835
2836     /**
2837      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2838      * function call will be the scope provided or the current node. The arguments to the function
2839      * will be the args provided or the current node. If the function returns false at any point,
2840      * the bubble is stopped.
2841      * @param {Function} fn The function to call
2842      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2843      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2844      */
2845     bubble : function(fn, scope, args){
2846         var p = this;
2847         while(p){
2848             if(fn.call(scope || p, args || p) === false){
2849                 break;
2850             }
2851             p = p.parentNode;
2852         }
2853     },
2854
2855     /**
2856      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2857      * function call will be the scope provided or the current node. The arguments to the function
2858      * will be the args provided or the current node. If the function returns false at any point,
2859      * the cascade is stopped on that branch.
2860      * @param {Function} fn The function to call
2861      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2862      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2863      */
2864     cascade : function(fn, scope, args){
2865         if(fn.call(scope || this, args || this) !== false){
2866             var cs = this.childNodes;
2867             for(var i = 0, len = cs.length; i < len; i++) {
2868                 cs[i].cascade(fn, scope, args);
2869             }
2870         }
2871     },
2872
2873     /**
2874      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2875      * function call will be the scope provided or the current node. The arguments to the function
2876      * will be the args provided or the current node. If the function returns false at any point,
2877      * the iteration stops.
2878      * @param {Function} fn The function to call
2879      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2880      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2881      */
2882     eachChild : function(fn, scope, args){
2883         var cs = this.childNodes;
2884         for(var i = 0, len = cs.length; i < len; i++) {
2885                 if(fn.call(scope || this, args || cs[i]) === false){
2886                     break;
2887                 }
2888         }
2889     },
2890
2891     /**
2892      * Finds the first child that has the attribute with the specified value.
2893      * @param {String} attribute The attribute name
2894      * @param {Mixed} value The value to search for
2895      * @return {Node} The found child or null if none was found
2896      */
2897     findChild : function(attribute, value){
2898         var cs = this.childNodes;
2899         for(var i = 0, len = cs.length; i < len; i++) {
2900                 if(cs[i].attributes[attribute] == value){
2901                     return cs[i];
2902                 }
2903         }
2904         return null;
2905     },
2906
2907     /**
2908      * Finds the first child by a custom function. The child matches if the function passed
2909      * returns true.
2910      * @param {Function} fn
2911      * @param {Object} scope (optional)
2912      * @return {Node} The found child or null if none was found
2913      */
2914     findChildBy : function(fn, scope){
2915         var cs = this.childNodes;
2916         for(var i = 0, len = cs.length; i < len; i++) {
2917                 if(fn.call(scope||cs[i], cs[i]) === true){
2918                     return cs[i];
2919                 }
2920         }
2921         return null;
2922     },
2923
2924     /**
2925      * Sorts this nodes children using the supplied sort function
2926      * @param {Function} fn
2927      * @param {Object} scope (optional)
2928      */
2929     sort : function(fn, scope){
2930         var cs = this.childNodes;
2931         var len = cs.length;
2932         if(len > 0){
2933             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2934             cs.sort(sortFn);
2935             for(var i = 0; i < len; i++){
2936                 var n = cs[i];
2937                 n.previousSibling = cs[i-1];
2938                 n.nextSibling = cs[i+1];
2939                 if(i == 0){
2940                     this.setFirstChild(n);
2941                 }
2942                 if(i == len-1){
2943                     this.setLastChild(n);
2944                 }
2945             }
2946         }
2947     },
2948
2949     /**
2950      * Returns true if this node is an ancestor (at any point) of the passed node.
2951      * @param {Node} node
2952      * @return {Boolean}
2953      */
2954     contains : function(node){
2955         return node.isAncestor(this);
2956     },
2957
2958     /**
2959      * Returns true if the passed node is an ancestor (at any point) of this node.
2960      * @param {Node} node
2961      * @return {Boolean}
2962      */
2963     isAncestor : function(node){
2964         var p = this.parentNode;
2965         while(p){
2966             if(p == node){
2967                 return true;
2968             }
2969             p = p.parentNode;
2970         }
2971         return false;
2972     },
2973
2974     toString : function(){
2975         return "[Node"+(this.id?" "+this.id:"")+"]";
2976     }
2977 });/*
2978  * Based on:
2979  * Ext JS Library 1.1.1
2980  * Copyright(c) 2006-2007, Ext JS, LLC.
2981  *
2982  * Originally Released Under LGPL - original licence link has changed is not relivant.
2983  *
2984  * Fork - LGPL
2985  * <script type="text/javascript">
2986  */
2987
2988
2989 /**
2990  * @class Roo.Shadow
2991  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2992  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
2993  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2994  * @constructor
2995  * Create a new Shadow
2996  * @param {Object} config The config object
2997  */
2998 Roo.Shadow = function(config){
2999     Roo.apply(this, config);
3000     if(typeof this.mode != "string"){
3001         this.mode = this.defaultMode;
3002     }
3003     var o = this.offset, a = {h: 0};
3004     var rad = Math.floor(this.offset/2);
3005     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3006         case "drop":
3007             a.w = 0;
3008             a.l = a.t = o;
3009             a.t -= 1;
3010             if(Roo.isIE){
3011                 a.l -= this.offset + rad;
3012                 a.t -= this.offset + rad;
3013                 a.w -= rad;
3014                 a.h -= rad;
3015                 a.t += 1;
3016             }
3017         break;
3018         case "sides":
3019             a.w = (o*2);
3020             a.l = -o;
3021             a.t = o-1;
3022             if(Roo.isIE){
3023                 a.l -= (this.offset - rad);
3024                 a.t -= this.offset + rad;
3025                 a.l += 1;
3026                 a.w -= (this.offset - rad)*2;
3027                 a.w -= rad + 1;
3028                 a.h -= 1;
3029             }
3030         break;
3031         case "frame":
3032             a.w = a.h = (o*2);
3033             a.l = a.t = -o;
3034             a.t += 1;
3035             a.h -= 2;
3036             if(Roo.isIE){
3037                 a.l -= (this.offset - rad);
3038                 a.t -= (this.offset - rad);
3039                 a.l += 1;
3040                 a.w -= (this.offset + rad + 1);
3041                 a.h -= (this.offset + rad);
3042                 a.h += 1;
3043             }
3044         break;
3045     };
3046
3047     this.adjusts = a;
3048 };
3049
3050 Roo.Shadow.prototype = {
3051     /**
3052      * @cfg {String} mode
3053      * The shadow display mode.  Supports the following options:<br />
3054      * sides: Shadow displays on both sides and bottom only<br />
3055      * frame: Shadow displays equally on all four sides<br />
3056      * drop: Traditional bottom-right drop shadow (default)
3057      */
3058     /**
3059      * @cfg {String} offset
3060      * The number of pixels to offset the shadow from the element (defaults to 4)
3061      */
3062     offset: 4,
3063
3064     // private
3065     defaultMode: "drop",
3066
3067     /**
3068      * Displays the shadow under the target element
3069      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
3070      */
3071     show : function(target){
3072         target = Roo.get(target);
3073         if(!this.el){
3074             this.el = Roo.Shadow.Pool.pull();
3075             if(this.el.dom.nextSibling != target.dom){
3076                 this.el.insertBefore(target);
3077             }
3078         }
3079         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
3080         if(Roo.isIE){
3081             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
3082         }
3083         this.realign(
3084             target.getLeft(true),
3085             target.getTop(true),
3086             target.getWidth(),
3087             target.getHeight()
3088         );
3089         this.el.dom.style.display = "block";
3090     },
3091
3092     /**
3093      * Returns true if the shadow is visible, else false
3094      */
3095     isVisible : function(){
3096         return this.el ? true : false;  
3097     },
3098
3099     /**
3100      * Direct alignment when values are already available. Show must be called at least once before
3101      * calling this method to ensure it is initialized.
3102      * @param {Number} left The target element left position
3103      * @param {Number} top The target element top position
3104      * @param {Number} width The target element width
3105      * @param {Number} height The target element height
3106      */
3107     realign : function(l, t, w, h){
3108         if(!this.el){
3109             return;
3110         }
3111         var a = this.adjusts, d = this.el.dom, s = d.style;
3112         var iea = 0;
3113         s.left = (l+a.l)+"px";
3114         s.top = (t+a.t)+"px";
3115         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
3116  
3117         if(s.width != sws || s.height != shs){
3118             s.width = sws;
3119             s.height = shs;
3120             if(!Roo.isIE){
3121                 var cn = d.childNodes;
3122                 var sww = Math.max(0, (sw-12))+"px";
3123                 cn[0].childNodes[1].style.width = sww;
3124                 cn[1].childNodes[1].style.width = sww;
3125                 cn[2].childNodes[1].style.width = sww;
3126                 cn[1].style.height = Math.max(0, (sh-12))+"px";
3127             }
3128         }
3129     },
3130
3131     /**
3132      * Hides this shadow
3133      */
3134     hide : function(){
3135         if(this.el){
3136             this.el.dom.style.display = "none";
3137             Roo.Shadow.Pool.push(this.el);
3138             delete this.el;
3139         }
3140     },
3141
3142     /**
3143      * Adjust the z-index of this shadow
3144      * @param {Number} zindex The new z-index
3145      */
3146     setZIndex : function(z){
3147         this.zIndex = z;
3148         if(this.el){
3149             this.el.setStyle("z-index", z);
3150         }
3151     }
3152 };
3153
3154 // Private utility class that manages the internal Shadow cache
3155 Roo.Shadow.Pool = function(){
3156     var p = [];
3157     var markup = Roo.isIE ?
3158                  '<div class="x-ie-shadow"></div>' :
3159                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
3160     return {
3161         pull : function(){
3162             var sh = p.shift();
3163             if(!sh){
3164                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
3165                 sh.autoBoxAdjust = false;
3166             }
3167             return sh;
3168         },
3169
3170         push : function(sh){
3171             p.push(sh);
3172         }
3173     };
3174 }();/*
3175  * Based on:
3176  * Ext JS Library 1.1.1
3177  * Copyright(c) 2006-2007, Ext JS, LLC.
3178  *
3179  * Originally Released Under LGPL - original licence link has changed is not relivant.
3180  *
3181  * Fork - LGPL
3182  * <script type="text/javascript">
3183  */
3184
3185
3186 /**
3187  * @class Roo.SplitBar
3188  * @extends Roo.util.Observable
3189  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
3190  * <br><br>
3191  * Usage:
3192  * <pre><code>
3193 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
3194                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
3195 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
3196 split.minSize = 100;
3197 split.maxSize = 600;
3198 split.animate = true;
3199 split.on('moved', splitterMoved);
3200 </code></pre>
3201  * @constructor
3202  * Create a new SplitBar
3203  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
3204  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
3205  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3206  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
3207                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
3208                         position of the SplitBar).
3209  */
3210 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
3211     
3212     /** @private */
3213     this.el = Roo.get(dragElement, true);
3214     this.el.dom.unselectable = "on";
3215     /** @private */
3216     this.resizingEl = Roo.get(resizingElement, true);
3217
3218     /**
3219      * @private
3220      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
3221      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
3222      * @type Number
3223      */
3224     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
3225     
3226     /**
3227      * The minimum size of the resizing element. (Defaults to 0)
3228      * @type Number
3229      */
3230     this.minSize = 0;
3231     
3232     /**
3233      * The maximum size of the resizing element. (Defaults to 2000)
3234      * @type Number
3235      */
3236     this.maxSize = 2000;
3237     
3238     /**
3239      * Whether to animate the transition to the new size
3240      * @type Boolean
3241      */
3242     this.animate = false;
3243     
3244     /**
3245      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
3246      * @type Boolean
3247      */
3248     this.useShim = false;
3249     
3250     /** @private */
3251     this.shim = null;
3252     
3253     if(!existingProxy){
3254         /** @private */
3255         this.proxy = Roo.SplitBar.createProxy(this.orientation);
3256     }else{
3257         this.proxy = Roo.get(existingProxy).dom;
3258     }
3259     /** @private */
3260     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
3261     
3262     /** @private */
3263     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
3264     
3265     /** @private */
3266     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
3267     
3268     /** @private */
3269     this.dragSpecs = {};
3270     
3271     /**
3272      * @private The adapter to use to positon and resize elements
3273      */
3274     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
3275     this.adapter.init(this);
3276     
3277     if(this.orientation == Roo.SplitBar.HORIZONTAL){
3278         /** @private */
3279         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
3280         this.el.addClass("x-splitbar-h");
3281     }else{
3282         /** @private */
3283         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
3284         this.el.addClass("x-splitbar-v");
3285     }
3286     
3287     this.addEvents({
3288         /**
3289          * @event resize
3290          * Fires when the splitter is moved (alias for {@link #event-moved})
3291          * @param {Roo.SplitBar} this
3292          * @param {Number} newSize the new width or height
3293          */
3294         "resize" : true,
3295         /**
3296          * @event moved
3297          * Fires when the splitter is moved
3298          * @param {Roo.SplitBar} this
3299          * @param {Number} newSize the new width or height
3300          */
3301         "moved" : true,
3302         /**
3303          * @event beforeresize
3304          * Fires before the splitter is dragged
3305          * @param {Roo.SplitBar} this
3306          */
3307         "beforeresize" : true,
3308
3309         "beforeapply" : true
3310     });
3311
3312     Roo.util.Observable.call(this);
3313 };
3314
3315 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
3316     onStartProxyDrag : function(x, y){
3317         this.fireEvent("beforeresize", this);
3318         if(!this.overlay){
3319             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
3320             o.unselectable();
3321             o.enableDisplayMode("block");
3322             // all splitbars share the same overlay
3323             Roo.SplitBar.prototype.overlay = o;
3324         }
3325         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
3326         this.overlay.show();
3327         Roo.get(this.proxy).setDisplayed("block");
3328         var size = this.adapter.getElementSize(this);
3329         this.activeMinSize = this.getMinimumSize();;
3330         this.activeMaxSize = this.getMaximumSize();;
3331         var c1 = size - this.activeMinSize;
3332         var c2 = Math.max(this.activeMaxSize - size, 0);
3333         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3334             this.dd.resetConstraints();
3335             this.dd.setXConstraint(
3336                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
3337                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
3338             );
3339             this.dd.setYConstraint(0, 0);
3340         }else{
3341             this.dd.resetConstraints();
3342             this.dd.setXConstraint(0, 0);
3343             this.dd.setYConstraint(
3344                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
3345                 this.placement == Roo.SplitBar.TOP ? c2 : c1
3346             );
3347          }
3348         this.dragSpecs.startSize = size;
3349         this.dragSpecs.startPoint = [x, y];
3350         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
3351     },
3352     
3353     /** 
3354      * @private Called after the drag operation by the DDProxy
3355      */
3356     onEndProxyDrag : function(e){
3357         Roo.get(this.proxy).setDisplayed(false);
3358         var endPoint = Roo.lib.Event.getXY(e);
3359         if(this.overlay){
3360             this.overlay.hide();
3361         }
3362         var newSize;
3363         if(this.orientation == Roo.SplitBar.HORIZONTAL){
3364             newSize = this.dragSpecs.startSize + 
3365                 (this.placement == Roo.SplitBar.LEFT ?
3366                     endPoint[0] - this.dragSpecs.startPoint[0] :
3367                     this.dragSpecs.startPoint[0] - endPoint[0]
3368                 );
3369         }else{
3370             newSize = this.dragSpecs.startSize + 
3371                 (this.placement == Roo.SplitBar.TOP ?
3372                     endPoint[1] - this.dragSpecs.startPoint[1] :
3373                     this.dragSpecs.startPoint[1] - endPoint[1]
3374                 );
3375         }
3376         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
3377         if(newSize != this.dragSpecs.startSize){
3378             if(this.fireEvent('beforeapply', this, newSize) !== false){
3379                 this.adapter.setElementSize(this, newSize);
3380                 this.fireEvent("moved", this, newSize);
3381                 this.fireEvent("resize", this, newSize);
3382             }
3383         }
3384     },
3385     
3386     /**
3387      * Get the adapter this SplitBar uses
3388      * @return The adapter object
3389      */
3390     getAdapter : function(){
3391         return this.adapter;
3392     },
3393     
3394     /**
3395      * Set the adapter this SplitBar uses
3396      * @param {Object} adapter A SplitBar adapter object
3397      */
3398     setAdapter : function(adapter){
3399         this.adapter = adapter;
3400         this.adapter.init(this);
3401     },
3402     
3403     /**
3404      * Gets the minimum size for the resizing element
3405      * @return {Number} The minimum size
3406      */
3407     getMinimumSize : function(){
3408         return this.minSize;
3409     },
3410     
3411     /**
3412      * Sets the minimum size for the resizing element
3413      * @param {Number} minSize The minimum size
3414      */
3415     setMinimumSize : function(minSize){
3416         this.minSize = minSize;
3417     },
3418     
3419     /**
3420      * Gets the maximum size for the resizing element
3421      * @return {Number} The maximum size
3422      */
3423     getMaximumSize : function(){
3424         return this.maxSize;
3425     },
3426     
3427     /**
3428      * Sets the maximum size for the resizing element
3429      * @param {Number} maxSize The maximum size
3430      */
3431     setMaximumSize : function(maxSize){
3432         this.maxSize = maxSize;
3433     },
3434     
3435     /**
3436      * Sets the initialize size for the resizing element
3437      * @param {Number} size The initial size
3438      */
3439     setCurrentSize : function(size){
3440         var oldAnimate = this.animate;
3441         this.animate = false;
3442         this.adapter.setElementSize(this, size);
3443         this.animate = oldAnimate;
3444     },
3445     
3446     /**
3447      * Destroy this splitbar. 
3448      * @param {Boolean} removeEl True to remove the element
3449      */
3450     destroy : function(removeEl){
3451         if(this.shim){
3452             this.shim.remove();
3453         }
3454         this.dd.unreg();
3455         this.proxy.parentNode.removeChild(this.proxy);
3456         if(removeEl){
3457             this.el.remove();
3458         }
3459     }
3460 });
3461
3462 /**
3463  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
3464  */
3465 Roo.SplitBar.createProxy = function(dir){
3466     var proxy = new Roo.Element(document.createElement("div"));
3467     proxy.unselectable();
3468     var cls = 'x-splitbar-proxy';
3469     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
3470     document.body.appendChild(proxy.dom);
3471     return proxy.dom;
3472 };
3473
3474 /** 
3475  * @class Roo.SplitBar.BasicLayoutAdapter
3476  * Default Adapter. It assumes the splitter and resizing element are not positioned
3477  * elements and only gets/sets the width of the element. Generally used for table based layouts.
3478  */
3479 Roo.SplitBar.BasicLayoutAdapter = function(){
3480 };
3481
3482 Roo.SplitBar.BasicLayoutAdapter.prototype = {
3483     // do nothing for now
3484     init : function(s){
3485     
3486     },
3487     /**
3488      * Called before drag operations to get the current size of the resizing element. 
3489      * @param {Roo.SplitBar} s The SplitBar using this adapter
3490      */
3491      getElementSize : function(s){
3492         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3493             return s.resizingEl.getWidth();
3494         }else{
3495             return s.resizingEl.getHeight();
3496         }
3497     },
3498     
3499     /**
3500      * Called after drag operations to set the size of the resizing element.
3501      * @param {Roo.SplitBar} s The SplitBar using this adapter
3502      * @param {Number} newSize The new size to set
3503      * @param {Function} onComplete A function to be invoked when resizing is complete
3504      */
3505     setElementSize : function(s, newSize, onComplete){
3506         if(s.orientation == Roo.SplitBar.HORIZONTAL){
3507             if(!s.animate){
3508                 s.resizingEl.setWidth(newSize);
3509                 if(onComplete){
3510                     onComplete(s, newSize);
3511                 }
3512             }else{
3513                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
3514             }
3515         }else{
3516             
3517             if(!s.animate){
3518                 s.resizingEl.setHeight(newSize);
3519                 if(onComplete){
3520                     onComplete(s, newSize);
3521                 }
3522             }else{
3523                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
3524             }
3525         }
3526     }
3527 };
3528
3529 /** 
3530  *@class Roo.SplitBar.AbsoluteLayoutAdapter
3531  * @extends Roo.SplitBar.BasicLayoutAdapter
3532  * Adapter that  moves the splitter element to align with the resized sizing element. 
3533  * Used with an absolute positioned SplitBar.
3534  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
3535  * document.body, make sure you assign an id to the body element.
3536  */
3537 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
3538     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
3539     this.container = Roo.get(container);
3540 };
3541
3542 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
3543     init : function(s){
3544         this.basic.init(s);
3545     },
3546     
3547     getElementSize : function(s){
3548         return this.basic.getElementSize(s);
3549     },
3550     
3551     setElementSize : function(s, newSize, onComplete){
3552         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
3553     },
3554     
3555     moveSplitter : function(s){
3556         var yes = Roo.SplitBar;
3557         switch(s.placement){
3558             case yes.LEFT:
3559                 s.el.setX(s.resizingEl.getRight());
3560                 break;
3561             case yes.RIGHT:
3562                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
3563                 break;
3564             case yes.TOP:
3565                 s.el.setY(s.resizingEl.getBottom());
3566                 break;
3567             case yes.BOTTOM:
3568                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
3569                 break;
3570         }
3571     }
3572 };
3573
3574 /**
3575  * Orientation constant - Create a vertical SplitBar
3576  * @static
3577  * @type Number
3578  */
3579 Roo.SplitBar.VERTICAL = 1;
3580
3581 /**
3582  * Orientation constant - Create a horizontal SplitBar
3583  * @static
3584  * @type Number
3585  */
3586 Roo.SplitBar.HORIZONTAL = 2;
3587
3588 /**
3589  * Placement constant - The resizing element is to the left of the splitter element
3590  * @static
3591  * @type Number
3592  */
3593 Roo.SplitBar.LEFT = 1;
3594
3595 /**
3596  * Placement constant - The resizing element is to the right of the splitter element
3597  * @static
3598  * @type Number
3599  */
3600 Roo.SplitBar.RIGHT = 2;
3601
3602 /**
3603  * Placement constant - The resizing element is positioned above the splitter element
3604  * @static
3605  * @type Number
3606  */
3607 Roo.SplitBar.TOP = 3;
3608
3609 /**
3610  * Placement constant - The resizing element is positioned under splitter element
3611  * @static
3612  * @type Number
3613  */
3614 Roo.SplitBar.BOTTOM = 4;
3615 /*
3616  * Based on:
3617  * Ext JS Library 1.1.1
3618  * Copyright(c) 2006-2007, Ext JS, LLC.
3619  *
3620  * Originally Released Under LGPL - original licence link has changed is not relivant.
3621  *
3622  * Fork - LGPL
3623  * <script type="text/javascript">
3624  */
3625
3626 /**
3627  * @class Roo.View
3628  * @extends Roo.util.Observable
3629  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
3630  * This class also supports single and multi selection modes. <br>
3631  * Create a data model bound view:
3632  <pre><code>
3633  var store = new Roo.data.Store(...);
3634
3635  var view = new Roo.View({
3636     el : "my-element",
3637     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
3638  
3639     singleSelect: true,
3640     selectedClass: "ydataview-selected",
3641     store: store
3642  });
3643
3644  // listen for node click?
3645  view.on("click", function(vw, index, node, e){
3646  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
3647  });
3648
3649  // load XML data
3650  dataModel.load("foobar.xml");
3651  </code></pre>
3652  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
3653  * <br><br>
3654  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
3655  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
3656  * 
3657  * Note: old style constructor is still suported (container, template, config)
3658  * 
3659  * @constructor
3660  * Create a new View
3661  * @param {Object} config The config object
3662  * 
3663  */
3664 Roo.View = function(config, depreciated_tpl, depreciated_config){
3665     
3666     this.parent = false;
3667     
3668     if (typeof(depreciated_tpl) == 'undefined') {
3669         // new way.. - universal constructor.
3670         Roo.apply(this, config);
3671         this.el  = Roo.get(this.el);
3672     } else {
3673         // old format..
3674         this.el  = Roo.get(config);
3675         this.tpl = depreciated_tpl;
3676         Roo.apply(this, depreciated_config);
3677     }
3678     this.wrapEl  = this.el.wrap().wrap();
3679     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
3680     
3681     
3682     if(typeof(this.tpl) == "string"){
3683         this.tpl = new Roo.Template(this.tpl);
3684     } else {
3685         // support xtype ctors..
3686         this.tpl = new Roo.factory(this.tpl, Roo);
3687     }
3688     
3689     
3690     this.tpl.compile();
3691     
3692     /** @private */
3693     this.addEvents({
3694         /**
3695          * @event beforeclick
3696          * Fires before a click is processed. Returns false to cancel the default action.
3697          * @param {Roo.View} this
3698          * @param {Number} index The index of the target node
3699          * @param {HTMLElement} node The target node
3700          * @param {Roo.EventObject} e The raw event object
3701          */
3702             "beforeclick" : true,
3703         /**
3704          * @event click
3705          * Fires when a template node is clicked.
3706          * @param {Roo.View} this
3707          * @param {Number} index The index of the target node
3708          * @param {HTMLElement} node The target node
3709          * @param {Roo.EventObject} e The raw event object
3710          */
3711             "click" : true,
3712         /**
3713          * @event dblclick
3714          * Fires when a template node is double clicked.
3715          * @param {Roo.View} this
3716          * @param {Number} index The index of the target node
3717          * @param {HTMLElement} node The target node
3718          * @param {Roo.EventObject} e The raw event object
3719          */
3720             "dblclick" : true,
3721         /**
3722          * @event contextmenu
3723          * Fires when a template node is right clicked.
3724          * @param {Roo.View} this
3725          * @param {Number} index The index of the target node
3726          * @param {HTMLElement} node The target node
3727          * @param {Roo.EventObject} e The raw event object
3728          */
3729             "contextmenu" : true,
3730         /**
3731          * @event selectionchange
3732          * Fires when the selected nodes change.
3733          * @param {Roo.View} this
3734          * @param {Array} selections Array of the selected nodes
3735          */
3736             "selectionchange" : true,
3737     
3738         /**
3739          * @event beforeselect
3740          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
3741          * @param {Roo.View} this
3742          * @param {HTMLElement} node The node to be selected
3743          * @param {Array} selections Array of currently selected nodes
3744          */
3745             "beforeselect" : true,
3746         /**
3747          * @event preparedata
3748          * Fires on every row to render, to allow you to change the data.
3749          * @param {Roo.View} this
3750          * @param {Object} data to be rendered (change this)
3751          */
3752           "preparedata" : true
3753           
3754           
3755         });
3756
3757
3758
3759     this.el.on({
3760         "click": this.onClick,
3761         "dblclick": this.onDblClick,
3762         "contextmenu": this.onContextMenu,
3763         scope:this
3764     });
3765
3766     this.selections = [];
3767     this.nodes = [];
3768     this.cmp = new Roo.CompositeElementLite([]);
3769     if(this.store){
3770         this.store = Roo.factory(this.store, Roo.data);
3771         this.setStore(this.store, true);
3772     }
3773     
3774     if ( this.footer && this.footer.xtype) {
3775            
3776          var fctr = this.wrapEl.appendChild(document.createElement("div"));
3777         
3778         this.footer.dataSource = this.store;
3779         this.footer.container = fctr;
3780         this.footer = Roo.factory(this.footer, Roo);
3781         fctr.insertFirst(this.el);
3782         
3783         // this is a bit insane - as the paging toolbar seems to detach the el..
3784 //        dom.parentNode.parentNode.parentNode
3785          // they get detached?
3786     }
3787     
3788     
3789     Roo.View.superclass.constructor.call(this);
3790     
3791     
3792 };
3793
3794 Roo.extend(Roo.View, Roo.util.Observable, {
3795     
3796      /**
3797      * @cfg {Roo.data.Store} store Data store to load data from.
3798      */
3799     store : false,
3800     
3801     /**
3802      * @cfg {String|Roo.Element} el The container element.
3803      */
3804     el : '',
3805     
3806     /**
3807      * @cfg {String|Roo.Template} tpl The template used by this View 
3808      */
3809     tpl : false,
3810     /**
3811      * @cfg {String} dataName the named area of the template to use as the data area
3812      *                          Works with domtemplates roo-name="name"
3813      */
3814     dataName: false,
3815     /**
3816      * @cfg {String} selectedClass The css class to add to selected nodes
3817      */
3818     selectedClass : "x-view-selected",
3819      /**
3820      * @cfg {String} emptyText The empty text to show when nothing is loaded.
3821      */
3822     emptyText : "",
3823     
3824     /**
3825      * @cfg {String} text to display on mask (default Loading)
3826      */
3827     mask : false,
3828     /**
3829      * @cfg {Boolean} multiSelect Allow multiple selection
3830      */
3831     multiSelect : false,
3832     /**
3833      * @cfg {Boolean} singleSelect Allow single selection
3834      */
3835     singleSelect:  false,
3836     
3837     /**
3838      * @cfg {Boolean} toggleSelect - selecting 
3839      */
3840     toggleSelect : false,
3841     
3842     /**
3843      * @cfg {Boolean} tickable - selecting 
3844      */
3845     tickable : false,
3846     
3847     /**
3848      * Returns the element this view is bound to.
3849      * @return {Roo.Element}
3850      */
3851     getEl : function(){
3852         return this.wrapEl;
3853     },
3854     
3855     
3856
3857     /**
3858      * Refreshes the view. - called by datachanged on the store. - do not call directly.
3859      */
3860     refresh : function(){
3861         //Roo.log('refresh');
3862         var t = this.tpl;
3863         
3864         // if we are using something like 'domtemplate', then
3865         // the what gets used is:
3866         // t.applySubtemplate(NAME, data, wrapping data..)
3867         // the outer template then get' applied with
3868         //     the store 'extra data'
3869         // and the body get's added to the
3870         //      roo-name="data" node?
3871         //      <span class='roo-tpl-{name}'></span> ?????
3872         
3873         
3874         
3875         this.clearSelections();
3876         this.el.update("");
3877         var html = [];
3878         var records = this.store.getRange();
3879         if(records.length < 1) {
3880             
3881             // is this valid??  = should it render a template??
3882             
3883             this.el.update(this.emptyText);
3884             return;
3885         }
3886         var el = this.el;
3887         if (this.dataName) {
3888             this.el.update(t.apply(this.store.meta)); //????
3889             el = this.el.child('.roo-tpl-' + this.dataName);
3890         }
3891         
3892         for(var i = 0, len = records.length; i < len; i++){
3893             var data = this.prepareData(records[i].data, i, records[i]);
3894             this.fireEvent("preparedata", this, data, i, records[i]);
3895             
3896             var d = Roo.apply({}, data);
3897             
3898             if(this.tickable){
3899                 Roo.apply(d, {'roo-id' : Roo.id()});
3900                 
3901                 var _this = this;
3902             
3903                 Roo.each(this.parent.item, function(item){
3904                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
3905                         return;
3906                     }
3907                     Roo.apply(d, {'roo-data-checked' : 'checked'});
3908                 });
3909             }
3910             
3911             html[html.length] = Roo.util.Format.trim(
3912                 this.dataName ?
3913                     t.applySubtemplate(this.dataName, d, this.store.meta) :
3914                     t.apply(d)
3915             );
3916         }
3917         
3918         
3919         
3920         el.update(html.join(""));
3921         this.nodes = el.dom.childNodes;
3922         this.updateIndexes(0);
3923     },
3924     
3925
3926     /**
3927      * Function to override to reformat the data that is sent to
3928      * the template for each node.
3929      * DEPRICATED - use the preparedata event handler.
3930      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
3931      * a JSON object for an UpdateManager bound view).
3932      */
3933     prepareData : function(data, index, record)
3934     {
3935         this.fireEvent("preparedata", this, data, index, record);
3936         return data;
3937     },
3938
3939     onUpdate : function(ds, record){
3940         // Roo.log('on update');   
3941         this.clearSelections();
3942         var index = this.store.indexOf(record);
3943         var n = this.nodes[index];
3944         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
3945         n.parentNode.removeChild(n);
3946         this.updateIndexes(index, index);
3947     },
3948
3949     
3950     
3951 // --------- FIXME     
3952     onAdd : function(ds, records, index)
3953     {
3954         //Roo.log(['on Add', ds, records, index] );        
3955         this.clearSelections();
3956         if(this.nodes.length == 0){
3957             this.refresh();
3958             return;
3959         }
3960         var n = this.nodes[index];
3961         for(var i = 0, len = records.length; i < len; i++){
3962             var d = this.prepareData(records[i].data, i, records[i]);
3963             if(n){
3964                 this.tpl.insertBefore(n, d);
3965             }else{
3966                 
3967                 this.tpl.append(this.el, d);
3968             }
3969         }
3970         this.updateIndexes(index);
3971     },
3972
3973     onRemove : function(ds, record, index){
3974        // Roo.log('onRemove');
3975         this.clearSelections();
3976         var el = this.dataName  ?
3977             this.el.child('.roo-tpl-' + this.dataName) :
3978             this.el; 
3979         
3980         el.dom.removeChild(this.nodes[index]);
3981         this.updateIndexes(index);
3982     },
3983
3984     /**
3985      * Refresh an individual node.
3986      * @param {Number} index
3987      */
3988     refreshNode : function(index){
3989         this.onUpdate(this.store, this.store.getAt(index));
3990     },
3991
3992     updateIndexes : function(startIndex, endIndex){
3993         var ns = this.nodes;
3994         startIndex = startIndex || 0;
3995         endIndex = endIndex || ns.length - 1;
3996         for(var i = startIndex; i <= endIndex; i++){
3997             ns[i].nodeIndex = i;
3998         }
3999     },
4000
4001     /**
4002      * Changes the data store this view uses and refresh the view.
4003      * @param {Store} store
4004      */
4005     setStore : function(store, initial){
4006         if(!initial && this.store){
4007             this.store.un("datachanged", this.refresh);
4008             this.store.un("add", this.onAdd);
4009             this.store.un("remove", this.onRemove);
4010             this.store.un("update", this.onUpdate);
4011             this.store.un("clear", this.refresh);
4012             this.store.un("beforeload", this.onBeforeLoad);
4013             this.store.un("load", this.onLoad);
4014             this.store.un("loadexception", this.onLoad);
4015         }
4016         if(store){
4017           
4018             store.on("datachanged", this.refresh, this);
4019             store.on("add", this.onAdd, this);
4020             store.on("remove", this.onRemove, this);
4021             store.on("update", this.onUpdate, this);
4022             store.on("clear", this.refresh, this);
4023             store.on("beforeload", this.onBeforeLoad, this);
4024             store.on("load", this.onLoad, this);
4025             store.on("loadexception", this.onLoad, this);
4026         }
4027         
4028         if(store){
4029             this.refresh();
4030         }
4031     },
4032     /**
4033      * onbeforeLoad - masks the loading area.
4034      *
4035      */
4036     onBeforeLoad : function(store,opts)
4037     {
4038          //Roo.log('onBeforeLoad');   
4039         if (!opts.add) {
4040             this.el.update("");
4041         }
4042         this.el.mask(this.mask ? this.mask : "Loading" ); 
4043     },
4044     onLoad : function ()
4045     {
4046         this.el.unmask();
4047     },
4048     
4049
4050     /**
4051      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
4052      * @param {HTMLElement} node
4053      * @return {HTMLElement} The template node
4054      */
4055     findItemFromChild : function(node){
4056         var el = this.dataName  ?
4057             this.el.child('.roo-tpl-' + this.dataName,true) :
4058             this.el.dom; 
4059         
4060         if(!node || node.parentNode == el){
4061                     return node;
4062             }
4063             var p = node.parentNode;
4064             while(p && p != el){
4065             if(p.parentNode == el){
4066                 return p;
4067             }
4068             p = p.parentNode;
4069         }
4070             return null;
4071     },
4072
4073     /** @ignore */
4074     onClick : function(e){
4075         var item = this.findItemFromChild(e.getTarget());
4076         if(item){
4077             var index = this.indexOf(item);
4078             if(this.onItemClick(item, index, e) !== false){
4079                 this.fireEvent("click", this, index, item, e);
4080             }
4081         }else{
4082             this.clearSelections();
4083         }
4084     },
4085
4086     /** @ignore */
4087     onContextMenu : function(e){
4088         var item = this.findItemFromChild(e.getTarget());
4089         if(item){
4090             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
4091         }
4092     },
4093
4094     /** @ignore */
4095     onDblClick : function(e){
4096         var item = this.findItemFromChild(e.getTarget());
4097         if(item){
4098             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
4099         }
4100     },
4101
4102     onItemClick : function(item, index, e)
4103     {
4104         if(this.fireEvent("beforeclick", this, index, item, e) === false){
4105             return false;
4106         }
4107         if (this.toggleSelect) {
4108             var m = this.isSelected(item) ? 'unselect' : 'select';
4109             //Roo.log(m);
4110             var _t = this;
4111             _t[m](item, true, false);
4112             return true;
4113         }
4114         if(this.multiSelect || this.singleSelect){
4115             if(this.multiSelect && e.shiftKey && this.lastSelection){
4116                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
4117             }else{
4118                 this.select(item, this.multiSelect && e.ctrlKey);
4119                 this.lastSelection = item;
4120             }
4121             
4122             if(!this.tickable){
4123                 e.preventDefault();
4124             }
4125             
4126         }
4127         return true;
4128     },
4129
4130     /**
4131      * Get the number of selected nodes.
4132      * @return {Number}
4133      */
4134     getSelectionCount : function(){
4135         return this.selections.length;
4136     },
4137
4138     /**
4139      * Get the currently selected nodes.
4140      * @return {Array} An array of HTMLElements
4141      */
4142     getSelectedNodes : function(){
4143         return this.selections;
4144     },
4145
4146     /**
4147      * Get the indexes of the selected nodes.
4148      * @return {Array}
4149      */
4150     getSelectedIndexes : function(){
4151         var indexes = [], s = this.selections;
4152         for(var i = 0, len = s.length; i < len; i++){
4153             indexes.push(s[i].nodeIndex);
4154         }
4155         return indexes;
4156     },
4157
4158     /**
4159      * Clear all selections
4160      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
4161      */
4162     clearSelections : function(suppressEvent){
4163         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
4164             this.cmp.elements = this.selections;
4165             this.cmp.removeClass(this.selectedClass);
4166             this.selections = [];
4167             if(!suppressEvent){
4168                 this.fireEvent("selectionchange", this, this.selections);
4169             }
4170         }
4171     },
4172
4173     /**
4174      * Returns true if the passed node is selected
4175      * @param {HTMLElement/Number} node The node or node index
4176      * @return {Boolean}
4177      */
4178     isSelected : function(node){
4179         var s = this.selections;
4180         if(s.length < 1){
4181             return false;
4182         }
4183         node = this.getNode(node);
4184         return s.indexOf(node) !== -1;
4185     },
4186
4187     /**
4188      * Selects nodes.
4189      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4190      * @param {Boolean} keepExisting (optional) true to keep existing selections
4191      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4192      */
4193     select : function(nodeInfo, keepExisting, suppressEvent){
4194         if(nodeInfo instanceof Array){
4195             if(!keepExisting){
4196                 this.clearSelections(true);
4197             }
4198             for(var i = 0, len = nodeInfo.length; i < len; i++){
4199                 this.select(nodeInfo[i], true, true);
4200             }
4201             return;
4202         } 
4203         var node = this.getNode(nodeInfo);
4204         if(!node || this.isSelected(node)){
4205             return; // already selected.
4206         }
4207         if(!keepExisting){
4208             this.clearSelections(true);
4209         }
4210         
4211         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
4212             Roo.fly(node).addClass(this.selectedClass);
4213             this.selections.push(node);
4214             if(!suppressEvent){
4215                 this.fireEvent("selectionchange", this, this.selections);
4216             }
4217         }
4218         
4219         
4220     },
4221       /**
4222      * Unselects nodes.
4223      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
4224      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
4225      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
4226      */
4227     unselect : function(nodeInfo, keepExisting, suppressEvent)
4228     {
4229         if(nodeInfo instanceof Array){
4230             Roo.each(this.selections, function(s) {
4231                 this.unselect(s, nodeInfo);
4232             }, this);
4233             return;
4234         }
4235         var node = this.getNode(nodeInfo);
4236         if(!node || !this.isSelected(node)){
4237             //Roo.log("not selected");
4238             return; // not selected.
4239         }
4240         // fireevent???
4241         var ns = [];
4242         Roo.each(this.selections, function(s) {
4243             if (s == node ) {
4244                 Roo.fly(node).removeClass(this.selectedClass);
4245
4246                 return;
4247             }
4248             ns.push(s);
4249         },this);
4250         
4251         this.selections= ns;
4252         this.fireEvent("selectionchange", this, this.selections);
4253     },
4254
4255     /**
4256      * Gets a template node.
4257      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4258      * @return {HTMLElement} The node or null if it wasn't found
4259      */
4260     getNode : function(nodeInfo){
4261         if(typeof nodeInfo == "string"){
4262             return document.getElementById(nodeInfo);
4263         }else if(typeof nodeInfo == "number"){
4264             return this.nodes[nodeInfo];
4265         }
4266         return nodeInfo;
4267     },
4268
4269     /**
4270      * Gets a range template nodes.
4271      * @param {Number} startIndex
4272      * @param {Number} endIndex
4273      * @return {Array} An array of nodes
4274      */
4275     getNodes : function(start, end){
4276         var ns = this.nodes;
4277         start = start || 0;
4278         end = typeof end == "undefined" ? ns.length - 1 : end;
4279         var nodes = [];
4280         if(start <= end){
4281             for(var i = start; i <= end; i++){
4282                 nodes.push(ns[i]);
4283             }
4284         } else{
4285             for(var i = start; i >= end; i--){
4286                 nodes.push(ns[i]);
4287             }
4288         }
4289         return nodes;
4290     },
4291
4292     /**
4293      * Finds the index of the passed node
4294      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
4295      * @return {Number} The index of the node or -1
4296      */
4297     indexOf : function(node){
4298         node = this.getNode(node);
4299         if(typeof node.nodeIndex == "number"){
4300             return node.nodeIndex;
4301         }
4302         var ns = this.nodes;
4303         for(var i = 0, len = ns.length; i < len; i++){
4304             if(ns[i] == node){
4305                 return i;
4306             }
4307         }
4308         return -1;
4309     }
4310 });
4311 /*
4312  * Based on:
4313  * Ext JS Library 1.1.1
4314  * Copyright(c) 2006-2007, Ext JS, LLC.
4315  *
4316  * Originally Released Under LGPL - original licence link has changed is not relivant.
4317  *
4318  * Fork - LGPL
4319  * <script type="text/javascript">
4320  */
4321
4322 /**
4323  * @class Roo.JsonView
4324  * @extends Roo.View
4325  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
4326 <pre><code>
4327 var view = new Roo.JsonView({
4328     container: "my-element",
4329     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
4330     multiSelect: true, 
4331     jsonRoot: "data" 
4332 });
4333
4334 // listen for node click?
4335 view.on("click", function(vw, index, node, e){
4336     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
4337 });
4338
4339 // direct load of JSON data
4340 view.load("foobar.php");
4341
4342 // Example from my blog list
4343 var tpl = new Roo.Template(
4344     '&lt;div class="entry"&gt;' +
4345     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
4346     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
4347     "&lt;/div&gt;&lt;hr /&gt;"
4348 );
4349
4350 var moreView = new Roo.JsonView({
4351     container :  "entry-list", 
4352     template : tpl,
4353     jsonRoot: "posts"
4354 });
4355 moreView.on("beforerender", this.sortEntries, this);
4356 moreView.load({
4357     url: "/blog/get-posts.php",
4358     params: "allposts=true",
4359     text: "Loading Blog Entries..."
4360 });
4361 </code></pre>
4362
4363 * Note: old code is supported with arguments : (container, template, config)
4364
4365
4366  * @constructor
4367  * Create a new JsonView
4368  * 
4369  * @param {Object} config The config object
4370  * 
4371  */
4372 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
4373     
4374     
4375     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
4376
4377     var um = this.el.getUpdateManager();
4378     um.setRenderer(this);
4379     um.on("update", this.onLoad, this);
4380     um.on("failure", this.onLoadException, this);
4381
4382     /**
4383      * @event beforerender
4384      * Fires before rendering of the downloaded JSON data.
4385      * @param {Roo.JsonView} this
4386      * @param {Object} data The JSON data loaded
4387      */
4388     /**
4389      * @event load
4390      * Fires when data is loaded.
4391      * @param {Roo.JsonView} this
4392      * @param {Object} data The JSON data loaded
4393      * @param {Object} response The raw Connect response object
4394      */
4395     /**
4396      * @event loadexception
4397      * Fires when loading fails.
4398      * @param {Roo.JsonView} this
4399      * @param {Object} response The raw Connect response object
4400      */
4401     this.addEvents({
4402         'beforerender' : true,
4403         'load' : true,
4404         'loadexception' : true
4405     });
4406 };
4407 Roo.extend(Roo.JsonView, Roo.View, {
4408     /**
4409      * @type {String} The root property in the loaded JSON object that contains the data
4410      */
4411     jsonRoot : "",
4412
4413     /**
4414      * Refreshes the view.
4415      */
4416     refresh : function(){
4417         this.clearSelections();
4418         this.el.update("");
4419         var html = [];
4420         var o = this.jsonData;
4421         if(o && o.length > 0){
4422             for(var i = 0, len = o.length; i < len; i++){
4423                 var data = this.prepareData(o[i], i, o);
4424                 html[html.length] = this.tpl.apply(data);
4425             }
4426         }else{
4427             html.push(this.emptyText);
4428         }
4429         this.el.update(html.join(""));
4430         this.nodes = this.el.dom.childNodes;
4431         this.updateIndexes(0);
4432     },
4433
4434     /**
4435      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
4436      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
4437      <pre><code>
4438      view.load({
4439          url: "your-url.php",
4440          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
4441          callback: yourFunction,
4442          scope: yourObject, //(optional scope)
4443          discardUrl: false,
4444          nocache: false,
4445          text: "Loading...",
4446          timeout: 30,
4447          scripts: false
4448      });
4449      </code></pre>
4450      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
4451      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
4452      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
4453      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
4454      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
4455      */
4456     load : function(){
4457         var um = this.el.getUpdateManager();
4458         um.update.apply(um, arguments);
4459     },
4460
4461     // note - render is a standard framework call...
4462     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
4463     render : function(el, response){
4464         
4465         this.clearSelections();
4466         this.el.update("");
4467         var o;
4468         try{
4469             if (response != '') {
4470                 o = Roo.util.JSON.decode(response.responseText);
4471                 if(this.jsonRoot){
4472                     
4473                     o = o[this.jsonRoot];
4474                 }
4475             }
4476         } catch(e){
4477         }
4478         /**
4479          * The current JSON data or null
4480          */
4481         this.jsonData = o;
4482         this.beforeRender();
4483         this.refresh();
4484     },
4485
4486 /**
4487  * Get the number of records in the current JSON dataset
4488  * @return {Number}
4489  */
4490     getCount : function(){
4491         return this.jsonData ? this.jsonData.length : 0;
4492     },
4493
4494 /**
4495  * Returns the JSON object for the specified node(s)
4496  * @param {HTMLElement/Array} node The node or an array of nodes
4497  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
4498  * you get the JSON object for the node
4499  */
4500     getNodeData : function(node){
4501         if(node instanceof Array){
4502             var data = [];
4503             for(var i = 0, len = node.length; i < len; i++){
4504                 data.push(this.getNodeData(node[i]));
4505             }
4506             return data;
4507         }
4508         return this.jsonData[this.indexOf(node)] || null;
4509     },
4510
4511     beforeRender : function(){
4512         this.snapshot = this.jsonData;
4513         if(this.sortInfo){
4514             this.sort.apply(this, this.sortInfo);
4515         }
4516         this.fireEvent("beforerender", this, this.jsonData);
4517     },
4518
4519     onLoad : function(el, o){
4520         this.fireEvent("load", this, this.jsonData, o);
4521     },
4522
4523     onLoadException : function(el, o){
4524         this.fireEvent("loadexception", this, o);
4525     },
4526
4527 /**
4528  * Filter the data by a specific property.
4529  * @param {String} property A property on your JSON objects
4530  * @param {String/RegExp} value Either string that the property values
4531  * should start with, or a RegExp to test against the property
4532  */
4533     filter : function(property, value){
4534         if(this.jsonData){
4535             var data = [];
4536             var ss = this.snapshot;
4537             if(typeof value == "string"){
4538                 var vlen = value.length;
4539                 if(vlen == 0){
4540                     this.clearFilter();
4541                     return;
4542                 }
4543                 value = value.toLowerCase();
4544                 for(var i = 0, len = ss.length; i < len; i++){
4545                     var o = ss[i];
4546                     if(o[property].substr(0, vlen).toLowerCase() == value){
4547                         data.push(o);
4548                     }
4549                 }
4550             } else if(value.exec){ // regex?
4551                 for(var i = 0, len = ss.length; i < len; i++){
4552                     var o = ss[i];
4553                     if(value.test(o[property])){
4554                         data.push(o);
4555                     }
4556                 }
4557             } else{
4558                 return;
4559             }
4560             this.jsonData = data;
4561             this.refresh();
4562         }
4563     },
4564
4565 /**
4566  * Filter by a function. The passed function will be called with each
4567  * object in the current dataset. If the function returns true the value is kept,
4568  * otherwise it is filtered.
4569  * @param {Function} fn
4570  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
4571  */
4572     filterBy : function(fn, scope){
4573         if(this.jsonData){
4574             var data = [];
4575             var ss = this.snapshot;
4576             for(var i = 0, len = ss.length; i < len; i++){
4577                 var o = ss[i];
4578                 if(fn.call(scope || this, o)){
4579                     data.push(o);
4580                 }
4581             }
4582             this.jsonData = data;
4583             this.refresh();
4584         }
4585     },
4586
4587 /**
4588  * Clears the current filter.
4589  */
4590     clearFilter : function(){
4591         if(this.snapshot && this.jsonData != this.snapshot){
4592             this.jsonData = this.snapshot;
4593             this.refresh();
4594         }
4595     },
4596
4597
4598 /**
4599  * Sorts the data for this view and refreshes it.
4600  * @param {String} property A property on your JSON objects to sort on
4601  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
4602  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
4603  */
4604     sort : function(property, dir, sortType){
4605         this.sortInfo = Array.prototype.slice.call(arguments, 0);
4606         if(this.jsonData){
4607             var p = property;
4608             var dsc = dir && dir.toLowerCase() == "desc";
4609             var f = function(o1, o2){
4610                 var v1 = sortType ? sortType(o1[p]) : o1[p];
4611                 var v2 = sortType ? sortType(o2[p]) : o2[p];
4612                 ;
4613                 if(v1 < v2){
4614                     return dsc ? +1 : -1;
4615                 } else if(v1 > v2){
4616                     return dsc ? -1 : +1;
4617                 } else{
4618                     return 0;
4619                 }
4620             };
4621             this.jsonData.sort(f);
4622             this.refresh();
4623             if(this.jsonData != this.snapshot){
4624                 this.snapshot.sort(f);
4625             }
4626         }
4627     }
4628 });/*
4629  * Based on:
4630  * Ext JS Library 1.1.1
4631  * Copyright(c) 2006-2007, Ext JS, LLC.
4632  *
4633  * Originally Released Under LGPL - original licence link has changed is not relivant.
4634  *
4635  * Fork - LGPL
4636  * <script type="text/javascript">
4637  */
4638  
4639
4640 /**
4641  * @class Roo.ColorPalette
4642  * @extends Roo.Component
4643  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
4644  * Here's an example of typical usage:
4645  * <pre><code>
4646 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
4647 cp.render('my-div');
4648
4649 cp.on('select', function(palette, selColor){
4650     // do something with selColor
4651 });
4652 </code></pre>
4653  * @constructor
4654  * Create a new ColorPalette
4655  * @param {Object} config The config object
4656  */
4657 Roo.ColorPalette = function(config){
4658     Roo.ColorPalette.superclass.constructor.call(this, config);
4659     this.addEvents({
4660         /**
4661              * @event select
4662              * Fires when a color is selected
4663              * @param {ColorPalette} this
4664              * @param {String} color The 6-digit color hex code (without the # symbol)
4665              */
4666         select: true
4667     });
4668
4669     if(this.handler){
4670         this.on("select", this.handler, this.scope, true);
4671     }
4672 };
4673 Roo.extend(Roo.ColorPalette, Roo.Component, {
4674     /**
4675      * @cfg {String} itemCls
4676      * The CSS class to apply to the containing element (defaults to "x-color-palette")
4677      */
4678     itemCls : "x-color-palette",
4679     /**
4680      * @cfg {String} value
4681      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
4682      * the hex codes are case-sensitive.
4683      */
4684     value : null,
4685     clickEvent:'click',
4686     // private
4687     ctype: "Roo.ColorPalette",
4688
4689     /**
4690      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
4691      */
4692     allowReselect : false,
4693
4694     /**
4695      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
4696      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
4697      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
4698      * of colors with the width setting until the box is symmetrical.</p>
4699      * <p>You can override individual colors if needed:</p>
4700      * <pre><code>
4701 var cp = new Roo.ColorPalette();
4702 cp.colors[0] = "FF0000";  // change the first box to red
4703 </code></pre>
4704
4705 Or you can provide a custom array of your own for complete control:
4706 <pre><code>
4707 var cp = new Roo.ColorPalette();
4708 cp.colors = ["000000", "993300", "333300"];
4709 </code></pre>
4710      * @type Array
4711      */
4712     colors : [
4713         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
4714         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
4715         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
4716         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
4717         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
4718     ],
4719
4720     // private
4721     onRender : function(container, position){
4722         var t = new Roo.MasterTemplate(
4723             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
4724         );
4725         var c = this.colors;
4726         for(var i = 0, len = c.length; i < len; i++){
4727             t.add([c[i]]);
4728         }
4729         var el = document.createElement("div");
4730         el.className = this.itemCls;
4731         t.overwrite(el);
4732         container.dom.insertBefore(el, position);
4733         this.el = Roo.get(el);
4734         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
4735         if(this.clickEvent != 'click'){
4736             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
4737         }
4738     },
4739
4740     // private
4741     afterRender : function(){
4742         Roo.ColorPalette.superclass.afterRender.call(this);
4743         if(this.value){
4744             var s = this.value;
4745             this.value = null;
4746             this.select(s);
4747         }
4748     },
4749
4750     // private
4751     handleClick : function(e, t){
4752         e.preventDefault();
4753         if(!this.disabled){
4754             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
4755             this.select(c.toUpperCase());
4756         }
4757     },
4758
4759     /**
4760      * Selects the specified color in the palette (fires the select event)
4761      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
4762      */
4763     select : function(color){
4764         color = color.replace("#", "");
4765         if(color != this.value || this.allowReselect){
4766             var el = this.el;
4767             if(this.value){
4768                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
4769             }
4770             el.child("a.color-"+color).addClass("x-color-palette-sel");
4771             this.value = color;
4772             this.fireEvent("select", this, color);
4773         }
4774     }
4775 });/*
4776  * Based on:
4777  * Ext JS Library 1.1.1
4778  * Copyright(c) 2006-2007, Ext JS, LLC.
4779  *
4780  * Originally Released Under LGPL - original licence link has changed is not relivant.
4781  *
4782  * Fork - LGPL
4783  * <script type="text/javascript">
4784  */
4785  
4786 /**
4787  * @class Roo.DatePicker
4788  * @extends Roo.Component
4789  * Simple date picker class.
4790  * @constructor
4791  * Create a new DatePicker
4792  * @param {Object} config The config object
4793  */
4794 Roo.DatePicker = function(config){
4795     Roo.DatePicker.superclass.constructor.call(this, config);
4796
4797     this.value = config && config.value ?
4798                  config.value.clearTime() : new Date().clearTime();
4799
4800     this.addEvents({
4801         /**
4802              * @event select
4803              * Fires when a date is selected
4804              * @param {DatePicker} this
4805              * @param {Date} date The selected date
4806              */
4807         'select': true,
4808         /**
4809              * @event monthchange
4810              * Fires when the displayed month changes 
4811              * @param {DatePicker} this
4812              * @param {Date} date The selected month
4813              */
4814         'monthchange': true
4815     });
4816
4817     if(this.handler){
4818         this.on("select", this.handler,  this.scope || this);
4819     }
4820     // build the disabledDatesRE
4821     if(!this.disabledDatesRE && this.disabledDates){
4822         var dd = this.disabledDates;
4823         var re = "(?:";
4824         for(var i = 0; i < dd.length; i++){
4825             re += dd[i];
4826             if(i != dd.length-1) {
4827                 re += "|";
4828             }
4829         }
4830         this.disabledDatesRE = new RegExp(re + ")");
4831     }
4832 };
4833
4834 Roo.extend(Roo.DatePicker, Roo.Component, {
4835     /**
4836      * @cfg {String} todayText
4837      * The text to display on the button that selects the current date (defaults to "Today")
4838      */
4839     todayText : "Today",
4840     /**
4841      * @cfg {String} okText
4842      * The text to display on the ok button
4843      */
4844     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
4845     /**
4846      * @cfg {String} cancelText
4847      * The text to display on the cancel button
4848      */
4849     cancelText : "Cancel",
4850     /**
4851      * @cfg {String} todayTip
4852      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
4853      */
4854     todayTip : "{0} (Spacebar)",
4855     /**
4856      * @cfg {Date} minDate
4857      * Minimum allowable date (JavaScript date object, defaults to null)
4858      */
4859     minDate : null,
4860     /**
4861      * @cfg {Date} maxDate
4862      * Maximum allowable date (JavaScript date object, defaults to null)
4863      */
4864     maxDate : null,
4865     /**
4866      * @cfg {String} minText
4867      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
4868      */
4869     minText : "This date is before the minimum date",
4870     /**
4871      * @cfg {String} maxText
4872      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
4873      */
4874     maxText : "This date is after the maximum date",
4875     /**
4876      * @cfg {String} format
4877      * The default date format string which can be overriden for localization support.  The format must be
4878      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
4879      */
4880     format : "m/d/y",
4881     /**
4882      * @cfg {Array} disabledDays
4883      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
4884      */
4885     disabledDays : null,
4886     /**
4887      * @cfg {String} disabledDaysText
4888      * The tooltip to display when the date falls on a disabled day (defaults to "")
4889      */
4890     disabledDaysText : "",
4891     /**
4892      * @cfg {RegExp} disabledDatesRE
4893      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
4894      */
4895     disabledDatesRE : null,
4896     /**
4897      * @cfg {String} disabledDatesText
4898      * The tooltip text to display when the date falls on a disabled date (defaults to "")
4899      */
4900     disabledDatesText : "",
4901     /**
4902      * @cfg {Boolean} constrainToViewport
4903      * True to constrain the date picker to the viewport (defaults to true)
4904      */
4905     constrainToViewport : true,
4906     /**
4907      * @cfg {Array} monthNames
4908      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
4909      */
4910     monthNames : Date.monthNames,
4911     /**
4912      * @cfg {Array} dayNames
4913      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
4914      */
4915     dayNames : Date.dayNames,
4916     /**
4917      * @cfg {String} nextText
4918      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
4919      */
4920     nextText: 'Next Month (Control+Right)',
4921     /**
4922      * @cfg {String} prevText
4923      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
4924      */
4925     prevText: 'Previous Month (Control+Left)',
4926     /**
4927      * @cfg {String} monthYearText
4928      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
4929      */
4930     monthYearText: 'Choose a month (Control+Up/Down to move years)',
4931     /**
4932      * @cfg {Number} startDay
4933      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
4934      */
4935     startDay : 0,
4936     /**
4937      * @cfg {Bool} showClear
4938      * Show a clear button (usefull for date form elements that can be blank.)
4939      */
4940     
4941     showClear: false,
4942     
4943     /**
4944      * Sets the value of the date field
4945      * @param {Date} value The date to set
4946      */
4947     setValue : function(value){
4948         var old = this.value;
4949         
4950         if (typeof(value) == 'string') {
4951          
4952             value = Date.parseDate(value, this.format);
4953         }
4954         if (!value) {
4955             value = new Date();
4956         }
4957         
4958         this.value = value.clearTime(true);
4959         if(this.el){
4960             this.update(this.value);
4961         }
4962     },
4963
4964     /**
4965      * Gets the current selected value of the date field
4966      * @return {Date} The selected date
4967      */
4968     getValue : function(){
4969         return this.value;
4970     },
4971
4972     // private
4973     focus : function(){
4974         if(this.el){
4975             this.update(this.activeDate);
4976         }
4977     },
4978
4979     // privateval
4980     onRender : function(container, position){
4981         
4982         var m = [
4983              '<table cellspacing="0">',
4984                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
4985                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
4986         var dn = this.dayNames;
4987         for(var i = 0; i < 7; i++){
4988             var d = this.startDay+i;
4989             if(d > 6){
4990                 d = d-7;
4991             }
4992             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
4993         }
4994         m[m.length] = "</tr></thead><tbody><tr>";
4995         for(var i = 0; i < 42; i++) {
4996             if(i % 7 == 0 && i != 0){
4997                 m[m.length] = "</tr><tr>";
4998             }
4999             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
5000         }
5001         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
5002             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
5003
5004         var el = document.createElement("div");
5005         el.className = "x-date-picker";
5006         el.innerHTML = m.join("");
5007
5008         container.dom.insertBefore(el, position);
5009
5010         this.el = Roo.get(el);
5011         this.eventEl = Roo.get(el.firstChild);
5012
5013         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
5014             handler: this.showPrevMonth,
5015             scope: this,
5016             preventDefault:true,
5017             stopDefault:true
5018         });
5019
5020         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
5021             handler: this.showNextMonth,
5022             scope: this,
5023             preventDefault:true,
5024             stopDefault:true
5025         });
5026
5027         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
5028
5029         this.monthPicker = this.el.down('div.x-date-mp');
5030         this.monthPicker.enableDisplayMode('block');
5031         
5032         var kn = new Roo.KeyNav(this.eventEl, {
5033             "left" : function(e){
5034                 e.ctrlKey ?
5035                     this.showPrevMonth() :
5036                     this.update(this.activeDate.add("d", -1));
5037             },
5038
5039             "right" : function(e){
5040                 e.ctrlKey ?
5041                     this.showNextMonth() :
5042                     this.update(this.activeDate.add("d", 1));
5043             },
5044
5045             "up" : function(e){
5046                 e.ctrlKey ?
5047                     this.showNextYear() :
5048                     this.update(this.activeDate.add("d", -7));
5049             },
5050
5051             "down" : function(e){
5052                 e.ctrlKey ?
5053                     this.showPrevYear() :
5054                     this.update(this.activeDate.add("d", 7));
5055             },
5056
5057             "pageUp" : function(e){
5058                 this.showNextMonth();
5059             },
5060
5061             "pageDown" : function(e){
5062                 this.showPrevMonth();
5063             },
5064
5065             "enter" : function(e){
5066                 e.stopPropagation();
5067                 return true;
5068             },
5069
5070             scope : this
5071         });
5072
5073         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
5074
5075         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
5076
5077         this.el.unselectable();
5078         
5079         this.cells = this.el.select("table.x-date-inner tbody td");
5080         this.textNodes = this.el.query("table.x-date-inner tbody span");
5081
5082         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
5083             text: "&#160;",
5084             tooltip: this.monthYearText
5085         });
5086
5087         this.mbtn.on('click', this.showMonthPicker, this);
5088         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
5089
5090
5091         var today = (new Date()).dateFormat(this.format);
5092         
5093         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
5094         if (this.showClear) {
5095             baseTb.add( new Roo.Toolbar.Fill());
5096         }
5097         baseTb.add({
5098             text: String.format(this.todayText, today),
5099             tooltip: String.format(this.todayTip, today),
5100             handler: this.selectToday,
5101             scope: this
5102         });
5103         
5104         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
5105             
5106         //});
5107         if (this.showClear) {
5108             
5109             baseTb.add( new Roo.Toolbar.Fill());
5110             baseTb.add({
5111                 text: '&#160;',
5112                 cls: 'x-btn-icon x-btn-clear',
5113                 handler: function() {
5114                     //this.value = '';
5115                     this.fireEvent("select", this, '');
5116                 },
5117                 scope: this
5118             });
5119         }
5120         
5121         
5122         if(Roo.isIE){
5123             this.el.repaint();
5124         }
5125         this.update(this.value);
5126     },
5127
5128     createMonthPicker : function(){
5129         if(!this.monthPicker.dom.firstChild){
5130             var buf = ['<table border="0" cellspacing="0">'];
5131             for(var i = 0; i < 6; i++){
5132                 buf.push(
5133                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
5134                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
5135                     i == 0 ?
5136                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
5137                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
5138                 );
5139             }
5140             buf.push(
5141                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
5142                     this.okText,
5143                     '</button><button type="button" class="x-date-mp-cancel">',
5144                     this.cancelText,
5145                     '</button></td></tr>',
5146                 '</table>'
5147             );
5148             this.monthPicker.update(buf.join(''));
5149             this.monthPicker.on('click', this.onMonthClick, this);
5150             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
5151
5152             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
5153             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
5154
5155             this.mpMonths.each(function(m, a, i){
5156                 i += 1;
5157                 if((i%2) == 0){
5158                     m.dom.xmonth = 5 + Math.round(i * .5);
5159                 }else{
5160                     m.dom.xmonth = Math.round((i-1) * .5);
5161                 }
5162             });
5163         }
5164     },
5165
5166     showMonthPicker : function(){
5167         this.createMonthPicker();
5168         var size = this.el.getSize();
5169         this.monthPicker.setSize(size);
5170         this.monthPicker.child('table').setSize(size);
5171
5172         this.mpSelMonth = (this.activeDate || this.value).getMonth();
5173         this.updateMPMonth(this.mpSelMonth);
5174         this.mpSelYear = (this.activeDate || this.value).getFullYear();
5175         this.updateMPYear(this.mpSelYear);
5176
5177         this.monthPicker.slideIn('t', {duration:.2});
5178     },
5179
5180     updateMPYear : function(y){
5181         this.mpyear = y;
5182         var ys = this.mpYears.elements;
5183         for(var i = 1; i <= 10; i++){
5184             var td = ys[i-1], y2;
5185             if((i%2) == 0){
5186                 y2 = y + Math.round(i * .5);
5187                 td.firstChild.innerHTML = y2;
5188                 td.xyear = y2;
5189             }else{
5190                 y2 = y - (5-Math.round(i * .5));
5191                 td.firstChild.innerHTML = y2;
5192                 td.xyear = y2;
5193             }
5194             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
5195         }
5196     },
5197
5198     updateMPMonth : function(sm){
5199         this.mpMonths.each(function(m, a, i){
5200             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
5201         });
5202     },
5203
5204     selectMPMonth: function(m){
5205         
5206     },
5207
5208     onMonthClick : function(e, t){
5209         e.stopEvent();
5210         var el = new Roo.Element(t), pn;
5211         if(el.is('button.x-date-mp-cancel')){
5212             this.hideMonthPicker();
5213         }
5214         else if(el.is('button.x-date-mp-ok')){
5215             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5216             this.hideMonthPicker();
5217         }
5218         else if(pn = el.up('td.x-date-mp-month', 2)){
5219             this.mpMonths.removeClass('x-date-mp-sel');
5220             pn.addClass('x-date-mp-sel');
5221             this.mpSelMonth = pn.dom.xmonth;
5222         }
5223         else if(pn = el.up('td.x-date-mp-year', 2)){
5224             this.mpYears.removeClass('x-date-mp-sel');
5225             pn.addClass('x-date-mp-sel');
5226             this.mpSelYear = pn.dom.xyear;
5227         }
5228         else if(el.is('a.x-date-mp-prev')){
5229             this.updateMPYear(this.mpyear-10);
5230         }
5231         else if(el.is('a.x-date-mp-next')){
5232             this.updateMPYear(this.mpyear+10);
5233         }
5234     },
5235
5236     onMonthDblClick : function(e, t){
5237         e.stopEvent();
5238         var el = new Roo.Element(t), pn;
5239         if(pn = el.up('td.x-date-mp-month', 2)){
5240             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
5241             this.hideMonthPicker();
5242         }
5243         else if(pn = el.up('td.x-date-mp-year', 2)){
5244             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
5245             this.hideMonthPicker();
5246         }
5247     },
5248
5249     hideMonthPicker : function(disableAnim){
5250         if(this.monthPicker){
5251             if(disableAnim === true){
5252                 this.monthPicker.hide();
5253             }else{
5254                 this.monthPicker.slideOut('t', {duration:.2});
5255             }
5256         }
5257     },
5258
5259     // private
5260     showPrevMonth : function(e){
5261         this.update(this.activeDate.add("mo", -1));
5262     },
5263
5264     // private
5265     showNextMonth : function(e){
5266         this.update(this.activeDate.add("mo", 1));
5267     },
5268
5269     // private
5270     showPrevYear : function(){
5271         this.update(this.activeDate.add("y", -1));
5272     },
5273
5274     // private
5275     showNextYear : function(){
5276         this.update(this.activeDate.add("y", 1));
5277     },
5278
5279     // private
5280     handleMouseWheel : function(e){
5281         var delta = e.getWheelDelta();
5282         if(delta > 0){
5283             this.showPrevMonth();
5284             e.stopEvent();
5285         } else if(delta < 0){
5286             this.showNextMonth();
5287             e.stopEvent();
5288         }
5289     },
5290
5291     // private
5292     handleDateClick : function(e, t){
5293         e.stopEvent();
5294         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
5295             this.setValue(new Date(t.dateValue));
5296             this.fireEvent("select", this, this.value);
5297         }
5298     },
5299
5300     // private
5301     selectToday : function(){
5302         this.setValue(new Date().clearTime());
5303         this.fireEvent("select", this, this.value);
5304     },
5305
5306     // private
5307     update : function(date)
5308     {
5309         var vd = this.activeDate;
5310         this.activeDate = date;
5311         if(vd && this.el){
5312             var t = date.getTime();
5313             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
5314                 this.cells.removeClass("x-date-selected");
5315                 this.cells.each(function(c){
5316                    if(c.dom.firstChild.dateValue == t){
5317                        c.addClass("x-date-selected");
5318                        setTimeout(function(){
5319                             try{c.dom.firstChild.focus();}catch(e){}
5320                        }, 50);
5321                        return false;
5322                    }
5323                 });
5324                 return;
5325             }
5326         }
5327         
5328         var days = date.getDaysInMonth();
5329         var firstOfMonth = date.getFirstDateOfMonth();
5330         var startingPos = firstOfMonth.getDay()-this.startDay;
5331
5332         if(startingPos <= this.startDay){
5333             startingPos += 7;
5334         }
5335
5336         var pm = date.add("mo", -1);
5337         var prevStart = pm.getDaysInMonth()-startingPos;
5338
5339         var cells = this.cells.elements;
5340         var textEls = this.textNodes;
5341         days += startingPos;
5342
5343         // convert everything to numbers so it's fast
5344         var day = 86400000;
5345         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
5346         var today = new Date().clearTime().getTime();
5347         var sel = date.clearTime().getTime();
5348         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
5349         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
5350         var ddMatch = this.disabledDatesRE;
5351         var ddText = this.disabledDatesText;
5352         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
5353         var ddaysText = this.disabledDaysText;
5354         var format = this.format;
5355
5356         var setCellClass = function(cal, cell){
5357             cell.title = "";
5358             var t = d.getTime();
5359             cell.firstChild.dateValue = t;
5360             if(t == today){
5361                 cell.className += " x-date-today";
5362                 cell.title = cal.todayText;
5363             }
5364             if(t == sel){
5365                 cell.className += " x-date-selected";
5366                 setTimeout(function(){
5367                     try{cell.firstChild.focus();}catch(e){}
5368                 }, 50);
5369             }
5370             // disabling
5371             if(t < min) {
5372                 cell.className = " x-date-disabled";
5373                 cell.title = cal.minText;
5374                 return;
5375             }
5376             if(t > max) {
5377                 cell.className = " x-date-disabled";
5378                 cell.title = cal.maxText;
5379                 return;
5380             }
5381             if(ddays){
5382                 if(ddays.indexOf(d.getDay()) != -1){
5383                     cell.title = ddaysText;
5384                     cell.className = " x-date-disabled";
5385                 }
5386             }
5387             if(ddMatch && format){
5388                 var fvalue = d.dateFormat(format);
5389                 if(ddMatch.test(fvalue)){
5390                     cell.title = ddText.replace("%0", fvalue);
5391                     cell.className = " x-date-disabled";
5392                 }
5393             }
5394         };
5395
5396         var i = 0;
5397         for(; i < startingPos; i++) {
5398             textEls[i].innerHTML = (++prevStart);
5399             d.setDate(d.getDate()+1);
5400             cells[i].className = "x-date-prevday";
5401             setCellClass(this, cells[i]);
5402         }
5403         for(; i < days; i++){
5404             intDay = i - startingPos + 1;
5405             textEls[i].innerHTML = (intDay);
5406             d.setDate(d.getDate()+1);
5407             cells[i].className = "x-date-active";
5408             setCellClass(this, cells[i]);
5409         }
5410         var extraDays = 0;
5411         for(; i < 42; i++) {
5412              textEls[i].innerHTML = (++extraDays);
5413              d.setDate(d.getDate()+1);
5414              cells[i].className = "x-date-nextday";
5415              setCellClass(this, cells[i]);
5416         }
5417
5418         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
5419         this.fireEvent('monthchange', this, date);
5420         
5421         if(!this.internalRender){
5422             var main = this.el.dom.firstChild;
5423             var w = main.offsetWidth;
5424             this.el.setWidth(w + this.el.getBorderWidth("lr"));
5425             Roo.fly(main).setWidth(w);
5426             this.internalRender = true;
5427             // opera does not respect the auto grow header center column
5428             // then, after it gets a width opera refuses to recalculate
5429             // without a second pass
5430             if(Roo.isOpera && !this.secondPass){
5431                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
5432                 this.secondPass = true;
5433                 this.update.defer(10, this, [date]);
5434             }
5435         }
5436         
5437         
5438     }
5439 });        /*
5440  * Based on:
5441  * Ext JS Library 1.1.1
5442  * Copyright(c) 2006-2007, Ext JS, LLC.
5443  *
5444  * Originally Released Under LGPL - original licence link has changed is not relivant.
5445  *
5446  * Fork - LGPL
5447  * <script type="text/javascript">
5448  */
5449 /**
5450  * @class Roo.TabPanel
5451  * @extends Roo.util.Observable
5452  * A lightweight tab container.
5453  * <br><br>
5454  * Usage:
5455  * <pre><code>
5456 // basic tabs 1, built from existing content
5457 var tabs = new Roo.TabPanel("tabs1");
5458 tabs.addTab("script", "View Script");
5459 tabs.addTab("markup", "View Markup");
5460 tabs.activate("script");
5461
5462 // more advanced tabs, built from javascript
5463 var jtabs = new Roo.TabPanel("jtabs");
5464 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
5465
5466 // set up the UpdateManager
5467 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
5468 var updater = tab2.getUpdateManager();
5469 updater.setDefaultUrl("ajax1.htm");
5470 tab2.on('activate', updater.refresh, updater, true);
5471
5472 // Use setUrl for Ajax loading
5473 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
5474 tab3.setUrl("ajax2.htm", null, true);
5475
5476 // Disabled tab
5477 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
5478 tab4.disable();
5479
5480 jtabs.activate("jtabs-1");
5481  * </code></pre>
5482  * @constructor
5483  * Create a new TabPanel.
5484  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
5485  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
5486  */
5487 Roo.TabPanel = function(container, config){
5488     /**
5489     * The container element for this TabPanel.
5490     * @type Roo.Element
5491     */
5492     this.el = Roo.get(container, true);
5493     if(config){
5494         if(typeof config == "boolean"){
5495             this.tabPosition = config ? "bottom" : "top";
5496         }else{
5497             Roo.apply(this, config);
5498         }
5499     }
5500     if(this.tabPosition == "bottom"){
5501         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5502         this.el.addClass("x-tabs-bottom");
5503     }
5504     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
5505     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
5506     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
5507     if(Roo.isIE){
5508         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
5509     }
5510     if(this.tabPosition != "bottom"){
5511         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
5512          * @type Roo.Element
5513          */
5514         this.bodyEl = Roo.get(this.createBody(this.el.dom));
5515         this.el.addClass("x-tabs-top");
5516     }
5517     this.items = [];
5518
5519     this.bodyEl.setStyle("position", "relative");
5520
5521     this.active = null;
5522     this.activateDelegate = this.activate.createDelegate(this);
5523
5524     this.addEvents({
5525         /**
5526          * @event tabchange
5527          * Fires when the active tab changes
5528          * @param {Roo.TabPanel} this
5529          * @param {Roo.TabPanelItem} activePanel The new active tab
5530          */
5531         "tabchange": true,
5532         /**
5533          * @event beforetabchange
5534          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
5535          * @param {Roo.TabPanel} this
5536          * @param {Object} e Set cancel to true on this object to cancel the tab change
5537          * @param {Roo.TabPanelItem} tab The tab being changed to
5538          */
5539         "beforetabchange" : true
5540     });
5541
5542     Roo.EventManager.onWindowResize(this.onResize, this);
5543     this.cpad = this.el.getPadding("lr");
5544     this.hiddenCount = 0;
5545
5546
5547     // toolbar on the tabbar support...
5548     if (this.toolbar) {
5549         var tcfg = this.toolbar;
5550         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
5551         this.toolbar = new Roo.Toolbar(tcfg);
5552         if (Roo.isSafari) {
5553             var tbl = tcfg.container.child('table', true);
5554             tbl.setAttribute('width', '100%');
5555         }
5556         
5557     }
5558    
5559
5560
5561     Roo.TabPanel.superclass.constructor.call(this);
5562 };
5563
5564 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
5565     /*
5566      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
5567      */
5568     tabPosition : "top",
5569     /*
5570      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
5571      */
5572     currentTabWidth : 0,
5573     /*
5574      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
5575      */
5576     minTabWidth : 40,
5577     /*
5578      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
5579      */
5580     maxTabWidth : 250,
5581     /*
5582      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
5583      */
5584     preferredTabWidth : 175,
5585     /*
5586      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
5587      */
5588     resizeTabs : false,
5589     /*
5590      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
5591      */
5592     monitorResize : true,
5593     /*
5594      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
5595      */
5596     toolbar : false,
5597
5598     /**
5599      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
5600      * @param {String} id The id of the div to use <b>or create</b>
5601      * @param {String} text The text for the tab
5602      * @param {String} content (optional) Content to put in the TabPanelItem body
5603      * @param {Boolean} closable (optional) True to create a close icon on the tab
5604      * @return {Roo.TabPanelItem} The created TabPanelItem
5605      */
5606     addTab : function(id, text, content, closable){
5607         var item = new Roo.TabPanelItem(this, id, text, closable);
5608         this.addTabItem(item);
5609         if(content){
5610             item.setContent(content);
5611         }
5612         return item;
5613     },
5614
5615     /**
5616      * Returns the {@link Roo.TabPanelItem} with the specified id/index
5617      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
5618      * @return {Roo.TabPanelItem}
5619      */
5620     getTab : function(id){
5621         return this.items[id];
5622     },
5623
5624     /**
5625      * Hides the {@link Roo.TabPanelItem} with the specified id/index
5626      * @param {String/Number} id The id or index of the TabPanelItem to hide.
5627      */
5628     hideTab : function(id){
5629         var t = this.items[id];
5630         if(!t.isHidden()){
5631            t.setHidden(true);
5632            this.hiddenCount++;
5633            this.autoSizeTabs();
5634         }
5635     },
5636
5637     /**
5638      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
5639      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
5640      */
5641     unhideTab : function(id){
5642         var t = this.items[id];
5643         if(t.isHidden()){
5644            t.setHidden(false);
5645            this.hiddenCount--;
5646            this.autoSizeTabs();
5647         }
5648     },
5649
5650     /**
5651      * Adds an existing {@link Roo.TabPanelItem}.
5652      * @param {Roo.TabPanelItem} item The TabPanelItem to add
5653      */
5654     addTabItem : function(item){
5655         this.items[item.id] = item;
5656         this.items.push(item);
5657         if(this.resizeTabs){
5658            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
5659            this.autoSizeTabs();
5660         }else{
5661             item.autoSize();
5662         }
5663     },
5664
5665     /**
5666      * Removes a {@link Roo.TabPanelItem}.
5667      * @param {String/Number} id The id or index of the TabPanelItem to remove.
5668      */
5669     removeTab : function(id){
5670         var items = this.items;
5671         var tab = items[id];
5672         if(!tab) { return; }
5673         var index = items.indexOf(tab);
5674         if(this.active == tab && items.length > 1){
5675             var newTab = this.getNextAvailable(index);
5676             if(newTab) {
5677                 newTab.activate();
5678             }
5679         }
5680         this.stripEl.dom.removeChild(tab.pnode.dom);
5681         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
5682             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
5683         }
5684         items.splice(index, 1);
5685         delete this.items[tab.id];
5686         tab.fireEvent("close", tab);
5687         tab.purgeListeners();
5688         this.autoSizeTabs();
5689     },
5690
5691     getNextAvailable : function(start){
5692         var items = this.items;
5693         var index = start;
5694         // look for a next tab that will slide over to
5695         // replace the one being removed
5696         while(index < items.length){
5697             var item = items[++index];
5698             if(item && !item.isHidden()){
5699                 return item;
5700             }
5701         }
5702         // if one isn't found select the previous tab (on the left)
5703         index = start;
5704         while(index >= 0){
5705             var item = items[--index];
5706             if(item && !item.isHidden()){
5707                 return item;
5708             }
5709         }
5710         return null;
5711     },
5712
5713     /**
5714      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
5715      * @param {String/Number} id The id or index of the TabPanelItem to disable.
5716      */
5717     disableTab : function(id){
5718         var tab = this.items[id];
5719         if(tab && this.active != tab){
5720             tab.disable();
5721         }
5722     },
5723
5724     /**
5725      * Enables a {@link Roo.TabPanelItem} that is disabled.
5726      * @param {String/Number} id The id or index of the TabPanelItem to enable.
5727      */
5728     enableTab : function(id){
5729         var tab = this.items[id];
5730         tab.enable();
5731     },
5732
5733     /**
5734      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
5735      * @param {String/Number} id The id or index of the TabPanelItem to activate.
5736      * @return {Roo.TabPanelItem} The TabPanelItem.
5737      */
5738     activate : function(id){
5739         var tab = this.items[id];
5740         if(!tab){
5741             return null;
5742         }
5743         if(tab == this.active || tab.disabled){
5744             return tab;
5745         }
5746         var e = {};
5747         this.fireEvent("beforetabchange", this, e, tab);
5748         if(e.cancel !== true && !tab.disabled){
5749             if(this.active){
5750                 this.active.hide();
5751             }
5752             this.active = this.items[id];
5753             this.active.show();
5754             this.fireEvent("tabchange", this, this.active);
5755         }
5756         return tab;
5757     },
5758
5759     /**
5760      * Gets the active {@link Roo.TabPanelItem}.
5761      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
5762      */
5763     getActiveTab : function(){
5764         return this.active;
5765     },
5766
5767     /**
5768      * Updates the tab body element to fit the height of the container element
5769      * for overflow scrolling
5770      * @param {Number} targetHeight (optional) Override the starting height from the elements height
5771      */
5772     syncHeight : function(targetHeight){
5773         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
5774         var bm = this.bodyEl.getMargins();
5775         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
5776         this.bodyEl.setHeight(newHeight);
5777         return newHeight;
5778     },
5779
5780     onResize : function(){
5781         if(this.monitorResize){
5782             this.autoSizeTabs();
5783         }
5784     },
5785
5786     /**
5787      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
5788      */
5789     beginUpdate : function(){
5790         this.updating = true;
5791     },
5792
5793     /**
5794      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
5795      */
5796     endUpdate : function(){
5797         this.updating = false;
5798         this.autoSizeTabs();
5799     },
5800
5801     /**
5802      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
5803      */
5804     autoSizeTabs : function(){
5805         var count = this.items.length;
5806         var vcount = count - this.hiddenCount;
5807         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
5808             return;
5809         }
5810         var w = Math.max(this.el.getWidth() - this.cpad, 10);
5811         var availWidth = Math.floor(w / vcount);
5812         var b = this.stripBody;
5813         if(b.getWidth() > w){
5814             var tabs = this.items;
5815             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
5816             if(availWidth < this.minTabWidth){
5817                 /*if(!this.sleft){    // incomplete scrolling code
5818                     this.createScrollButtons();
5819                 }
5820                 this.showScroll();
5821                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
5822             }
5823         }else{
5824             if(this.currentTabWidth < this.preferredTabWidth){
5825                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
5826             }
5827         }
5828     },
5829
5830     /**
5831      * Returns the number of tabs in this TabPanel.
5832      * @return {Number}
5833      */
5834      getCount : function(){
5835          return this.items.length;
5836      },
5837
5838     /**
5839      * Resizes all the tabs to the passed width
5840      * @param {Number} The new width
5841      */
5842     setTabWidth : function(width){
5843         this.currentTabWidth = width;
5844         for(var i = 0, len = this.items.length; i < len; i++) {
5845                 if(!this.items[i].isHidden()) {
5846                 this.items[i].setWidth(width);
5847             }
5848         }
5849     },
5850
5851     /**
5852      * Destroys this TabPanel
5853      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
5854      */
5855     destroy : function(removeEl){
5856         Roo.EventManager.removeResizeListener(this.onResize, this);
5857         for(var i = 0, len = this.items.length; i < len; i++){
5858             this.items[i].purgeListeners();
5859         }
5860         if(removeEl === true){
5861             this.el.update("");
5862             this.el.remove();
5863         }
5864     }
5865 });
5866
5867 /**
5868  * @class Roo.TabPanelItem
5869  * @extends Roo.util.Observable
5870  * Represents an individual item (tab plus body) in a TabPanel.
5871  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
5872  * @param {String} id The id of this TabPanelItem
5873  * @param {String} text The text for the tab of this TabPanelItem
5874  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
5875  */
5876 Roo.TabPanelItem = function(tabPanel, id, text, closable){
5877     /**
5878      * The {@link Roo.TabPanel} this TabPanelItem belongs to
5879      * @type Roo.TabPanel
5880      */
5881     this.tabPanel = tabPanel;
5882     /**
5883      * The id for this TabPanelItem
5884      * @type String
5885      */
5886     this.id = id;
5887     /** @private */
5888     this.disabled = false;
5889     /** @private */
5890     this.text = text;
5891     /** @private */
5892     this.loaded = false;
5893     this.closable = closable;
5894
5895     /**
5896      * The body element for this TabPanelItem.
5897      * @type Roo.Element
5898      */
5899     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
5900     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
5901     this.bodyEl.setStyle("display", "block");
5902     this.bodyEl.setStyle("zoom", "1");
5903     this.hideAction();
5904
5905     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
5906     /** @private */
5907     this.el = Roo.get(els.el, true);
5908     this.inner = Roo.get(els.inner, true);
5909     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
5910     this.pnode = Roo.get(els.el.parentNode, true);
5911     this.el.on("mousedown", this.onTabMouseDown, this);
5912     this.el.on("click", this.onTabClick, this);
5913     /** @private */
5914     if(closable){
5915         var c = Roo.get(els.close, true);
5916         c.dom.title = this.closeText;
5917         c.addClassOnOver("close-over");
5918         c.on("click", this.closeClick, this);
5919      }
5920
5921     this.addEvents({
5922          /**
5923          * @event activate
5924          * Fires when this tab becomes the active tab.
5925          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5926          * @param {Roo.TabPanelItem} this
5927          */
5928         "activate": true,
5929         /**
5930          * @event beforeclose
5931          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
5932          * @param {Roo.TabPanelItem} this
5933          * @param {Object} e Set cancel to true on this object to cancel the close.
5934          */
5935         "beforeclose": true,
5936         /**
5937          * @event close
5938          * Fires when this tab is closed.
5939          * @param {Roo.TabPanelItem} this
5940          */
5941          "close": true,
5942         /**
5943          * @event deactivate
5944          * Fires when this tab is no longer the active tab.
5945          * @param {Roo.TabPanel} tabPanel The parent TabPanel
5946          * @param {Roo.TabPanelItem} this
5947          */
5948          "deactivate" : true
5949     });
5950     this.hidden = false;
5951
5952     Roo.TabPanelItem.superclass.constructor.call(this);
5953 };
5954
5955 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
5956     purgeListeners : function(){
5957        Roo.util.Observable.prototype.purgeListeners.call(this);
5958        this.el.removeAllListeners();
5959     },
5960     /**
5961      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
5962      */
5963     show : function(){
5964         this.pnode.addClass("on");
5965         this.showAction();
5966         if(Roo.isOpera){
5967             this.tabPanel.stripWrap.repaint();
5968         }
5969         this.fireEvent("activate", this.tabPanel, this);
5970     },
5971
5972     /**
5973      * Returns true if this tab is the active tab.
5974      * @return {Boolean}
5975      */
5976     isActive : function(){
5977         return this.tabPanel.getActiveTab() == this;
5978     },
5979
5980     /**
5981      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
5982      */
5983     hide : function(){
5984         this.pnode.removeClass("on");
5985         this.hideAction();
5986         this.fireEvent("deactivate", this.tabPanel, this);
5987     },
5988
5989     hideAction : function(){
5990         this.bodyEl.hide();
5991         this.bodyEl.setStyle("position", "absolute");
5992         this.bodyEl.setLeft("-20000px");
5993         this.bodyEl.setTop("-20000px");
5994     },
5995
5996     showAction : function(){
5997         this.bodyEl.setStyle("position", "relative");
5998         this.bodyEl.setTop("");
5999         this.bodyEl.setLeft("");
6000         this.bodyEl.show();
6001     },
6002
6003     /**
6004      * Set the tooltip for the tab.
6005      * @param {String} tooltip The tab's tooltip
6006      */
6007     setTooltip : function(text){
6008         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
6009             this.textEl.dom.qtip = text;
6010             this.textEl.dom.removeAttribute('title');
6011         }else{
6012             this.textEl.dom.title = text;
6013         }
6014     },
6015
6016     onTabClick : function(e){
6017         e.preventDefault();
6018         this.tabPanel.activate(this.id);
6019     },
6020
6021     onTabMouseDown : function(e){
6022         e.preventDefault();
6023         this.tabPanel.activate(this.id);
6024     },
6025
6026     getWidth : function(){
6027         return this.inner.getWidth();
6028     },
6029
6030     setWidth : function(width){
6031         var iwidth = width - this.pnode.getPadding("lr");
6032         this.inner.setWidth(iwidth);
6033         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
6034         this.pnode.setWidth(width);
6035     },
6036
6037     /**
6038      * Show or hide the tab
6039      * @param {Boolean} hidden True to hide or false to show.
6040      */
6041     setHidden : function(hidden){
6042         this.hidden = hidden;
6043         this.pnode.setStyle("display", hidden ? "none" : "");
6044     },
6045
6046     /**
6047      * Returns true if this tab is "hidden"
6048      * @return {Boolean}
6049      */
6050     isHidden : function(){
6051         return this.hidden;
6052     },
6053
6054     /**
6055      * Returns the text for this tab
6056      * @return {String}
6057      */
6058     getText : function(){
6059         return this.text;
6060     },
6061
6062     autoSize : function(){
6063         //this.el.beginMeasure();
6064         this.textEl.setWidth(1);
6065         /*
6066          *  #2804 [new] Tabs in Roojs
6067          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
6068          */
6069         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
6070         //this.el.endMeasure();
6071     },
6072
6073     /**
6074      * Sets the text for the tab (Note: this also sets the tooltip text)
6075      * @param {String} text The tab's text and tooltip
6076      */
6077     setText : function(text){
6078         this.text = text;
6079         this.textEl.update(text);
6080         this.setTooltip(text);
6081         if(!this.tabPanel.resizeTabs){
6082             this.autoSize();
6083         }
6084     },
6085     /**
6086      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
6087      */
6088     activate : function(){
6089         this.tabPanel.activate(this.id);
6090     },
6091
6092     /**
6093      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
6094      */
6095     disable : function(){
6096         if(this.tabPanel.active != this){
6097             this.disabled = true;
6098             this.pnode.addClass("disabled");
6099         }
6100     },
6101
6102     /**
6103      * Enables this TabPanelItem if it was previously disabled.
6104      */
6105     enable : function(){
6106         this.disabled = false;
6107         this.pnode.removeClass("disabled");
6108     },
6109
6110     /**
6111      * Sets the content for this TabPanelItem.
6112      * @param {String} content The content
6113      * @param {Boolean} loadScripts true to look for and load scripts
6114      */
6115     setContent : function(content, loadScripts){
6116         this.bodyEl.update(content, loadScripts);
6117     },
6118
6119     /**
6120      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
6121      * @return {Roo.UpdateManager} The UpdateManager
6122      */
6123     getUpdateManager : function(){
6124         return this.bodyEl.getUpdateManager();
6125     },
6126
6127     /**
6128      * Set a URL to be used to load the content for this TabPanelItem.
6129      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
6130      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
6131      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
6132      * @return {Roo.UpdateManager} The UpdateManager
6133      */
6134     setUrl : function(url, params, loadOnce){
6135         if(this.refreshDelegate){
6136             this.un('activate', this.refreshDelegate);
6137         }
6138         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
6139         this.on("activate", this.refreshDelegate);
6140         return this.bodyEl.getUpdateManager();
6141     },
6142
6143     /** @private */
6144     _handleRefresh : function(url, params, loadOnce){
6145         if(!loadOnce || !this.loaded){
6146             var updater = this.bodyEl.getUpdateManager();
6147             updater.update(url, params, this._setLoaded.createDelegate(this));
6148         }
6149     },
6150
6151     /**
6152      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
6153      *   Will fail silently if the setUrl method has not been called.
6154      *   This does not activate the panel, just updates its content.
6155      */
6156     refresh : function(){
6157         if(this.refreshDelegate){
6158            this.loaded = false;
6159            this.refreshDelegate();
6160         }
6161     },
6162
6163     /** @private */
6164     _setLoaded : function(){
6165         this.loaded = true;
6166     },
6167
6168     /** @private */
6169     closeClick : function(e){
6170         var o = {};
6171         e.stopEvent();
6172         this.fireEvent("beforeclose", this, o);
6173         if(o.cancel !== true){
6174             this.tabPanel.removeTab(this.id);
6175         }
6176     },
6177     /**
6178      * The text displayed in the tooltip for the close icon.
6179      * @type String
6180      */
6181     closeText : "Close this tab"
6182 });
6183
6184 /** @private */
6185 Roo.TabPanel.prototype.createStrip = function(container){
6186     var strip = document.createElement("div");
6187     strip.className = "x-tabs-wrap";
6188     container.appendChild(strip);
6189     return strip;
6190 };
6191 /** @private */
6192 Roo.TabPanel.prototype.createStripList = function(strip){
6193     // div wrapper for retard IE
6194     // returns the "tr" element.
6195     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
6196         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
6197         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
6198     return strip.firstChild.firstChild.firstChild.firstChild;
6199 };
6200 /** @private */
6201 Roo.TabPanel.prototype.createBody = function(container){
6202     var body = document.createElement("div");
6203     Roo.id(body, "tab-body");
6204     Roo.fly(body).addClass("x-tabs-body");
6205     container.appendChild(body);
6206     return body;
6207 };
6208 /** @private */
6209 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
6210     var body = Roo.getDom(id);
6211     if(!body){
6212         body = document.createElement("div");
6213         body.id = id;
6214     }
6215     Roo.fly(body).addClass("x-tabs-item-body");
6216     bodyEl.insertBefore(body, bodyEl.firstChild);
6217     return body;
6218 };
6219 /** @private */
6220 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
6221     var td = document.createElement("td");
6222     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
6223     //stripEl.appendChild(td);
6224     if(closable){
6225         td.className = "x-tabs-closable";
6226         if(!this.closeTpl){
6227             this.closeTpl = new Roo.Template(
6228                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6229                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
6230                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
6231             );
6232         }
6233         var el = this.closeTpl.overwrite(td, {"text": text});
6234         var close = el.getElementsByTagName("div")[0];
6235         var inner = el.getElementsByTagName("em")[0];
6236         return {"el": el, "close": close, "inner": inner};
6237     } else {
6238         if(!this.tabTpl){
6239             this.tabTpl = new Roo.Template(
6240                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
6241                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
6242             );
6243         }
6244         var el = this.tabTpl.overwrite(td, {"text": text});
6245         var inner = el.getElementsByTagName("em")[0];
6246         return {"el": el, "inner": inner};
6247     }
6248 };/*
6249  * Based on:
6250  * Ext JS Library 1.1.1
6251  * Copyright(c) 2006-2007, Ext JS, LLC.
6252  *
6253  * Originally Released Under LGPL - original licence link has changed is not relivant.
6254  *
6255  * Fork - LGPL
6256  * <script type="text/javascript">
6257  */
6258
6259 /**
6260  * @class Roo.Button
6261  * @extends Roo.util.Observable
6262  * Simple Button class
6263  * @cfg {String} text The button text
6264  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
6265  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
6266  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
6267  * @cfg {Object} scope The scope of the handler
6268  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
6269  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
6270  * @cfg {Boolean} hidden True to start hidden (defaults to false)
6271  * @cfg {Boolean} disabled True to start disabled (defaults to false)
6272  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
6273  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
6274    applies if enableToggle = true)
6275  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
6276  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
6277   an {@link Roo.util.ClickRepeater} config object (defaults to false).
6278  * @constructor
6279  * Create a new button
6280  * @param {Object} config The config object
6281  */
6282 Roo.Button = function(renderTo, config)
6283 {
6284     if (!config) {
6285         config = renderTo;
6286         renderTo = config.renderTo || false;
6287     }
6288     
6289     Roo.apply(this, config);
6290     this.addEvents({
6291         /**
6292              * @event click
6293              * Fires when this button is clicked
6294              * @param {Button} this
6295              * @param {EventObject} e The click event
6296              */
6297             "click" : true,
6298         /**
6299              * @event toggle
6300              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
6301              * @param {Button} this
6302              * @param {Boolean} pressed
6303              */
6304             "toggle" : true,
6305         /**
6306              * @event mouseover
6307              * Fires when the mouse hovers over the button
6308              * @param {Button} this
6309              * @param {Event} e The event object
6310              */
6311         'mouseover' : true,
6312         /**
6313              * @event mouseout
6314              * Fires when the mouse exits the button
6315              * @param {Button} this
6316              * @param {Event} e The event object
6317              */
6318         'mouseout': true,
6319          /**
6320              * @event render
6321              * Fires when the button is rendered
6322              * @param {Button} this
6323              */
6324         'render': true
6325     });
6326     if(this.menu){
6327         this.menu = Roo.menu.MenuMgr.get(this.menu);
6328     }
6329     // register listeners first!!  - so render can be captured..
6330     Roo.util.Observable.call(this);
6331     if(renderTo){
6332         this.render(renderTo);
6333     }
6334     
6335   
6336 };
6337
6338 Roo.extend(Roo.Button, Roo.util.Observable, {
6339     /**
6340      * 
6341      */
6342     
6343     /**
6344      * Read-only. True if this button is hidden
6345      * @type Boolean
6346      */
6347     hidden : false,
6348     /**
6349      * Read-only. True if this button is disabled
6350      * @type Boolean
6351      */
6352     disabled : false,
6353     /**
6354      * Read-only. True if this button is pressed (only if enableToggle = true)
6355      * @type Boolean
6356      */
6357     pressed : false,
6358
6359     /**
6360      * @cfg {Number} tabIndex 
6361      * The DOM tabIndex for this button (defaults to undefined)
6362      */
6363     tabIndex : undefined,
6364
6365     /**
6366      * @cfg {Boolean} enableToggle
6367      * True to enable pressed/not pressed toggling (defaults to false)
6368      */
6369     enableToggle: false,
6370     /**
6371      * @cfg {Mixed} menu
6372      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
6373      */
6374     menu : undefined,
6375     /**
6376      * @cfg {String} menuAlign
6377      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
6378      */
6379     menuAlign : "tl-bl?",
6380
6381     /**
6382      * @cfg {String} iconCls
6383      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
6384      */
6385     iconCls : undefined,
6386     /**
6387      * @cfg {String} type
6388      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
6389      */
6390     type : 'button',
6391
6392     // private
6393     menuClassTarget: 'tr',
6394
6395     /**
6396      * @cfg {String} clickEvent
6397      * The type of event to map to the button's event handler (defaults to 'click')
6398      */
6399     clickEvent : 'click',
6400
6401     /**
6402      * @cfg {Boolean} handleMouseEvents
6403      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
6404      */
6405     handleMouseEvents : true,
6406
6407     /**
6408      * @cfg {String} tooltipType
6409      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
6410      */
6411     tooltipType : 'qtip',
6412
6413     /**
6414      * @cfg {String} cls
6415      * A CSS class to apply to the button's main element.
6416      */
6417     
6418     /**
6419      * @cfg {Roo.Template} template (Optional)
6420      * An {@link Roo.Template} with which to create the Button's main element. This Template must
6421      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
6422      * require code modifications if required elements (e.g. a button) aren't present.
6423      */
6424
6425     // private
6426     render : function(renderTo){
6427         var btn;
6428         if(this.hideParent){
6429             this.parentEl = Roo.get(renderTo);
6430         }
6431         if(!this.dhconfig){
6432             if(!this.template){
6433                 if(!Roo.Button.buttonTemplate){
6434                     // hideous table template
6435                     Roo.Button.buttonTemplate = new Roo.Template(
6436                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
6437                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
6438                         "</tr></tbody></table>");
6439                 }
6440                 this.template = Roo.Button.buttonTemplate;
6441             }
6442             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
6443             var btnEl = btn.child("button:first");
6444             btnEl.on('focus', this.onFocus, this);
6445             btnEl.on('blur', this.onBlur, this);
6446             if(this.cls){
6447                 btn.addClass(this.cls);
6448             }
6449             if(this.icon){
6450                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
6451             }
6452             if(this.iconCls){
6453                 btnEl.addClass(this.iconCls);
6454                 if(!this.cls){
6455                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6456                 }
6457             }
6458             if(this.tabIndex !== undefined){
6459                 btnEl.dom.tabIndex = this.tabIndex;
6460             }
6461             if(this.tooltip){
6462                 if(typeof this.tooltip == 'object'){
6463                     Roo.QuickTips.tips(Roo.apply({
6464                           target: btnEl.id
6465                     }, this.tooltip));
6466                 } else {
6467                     btnEl.dom[this.tooltipType] = this.tooltip;
6468                 }
6469             }
6470         }else{
6471             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
6472         }
6473         this.el = btn;
6474         if(this.id){
6475             this.el.dom.id = this.el.id = this.id;
6476         }
6477         if(this.menu){
6478             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
6479             this.menu.on("show", this.onMenuShow, this);
6480             this.menu.on("hide", this.onMenuHide, this);
6481         }
6482         btn.addClass("x-btn");
6483         if(Roo.isIE && !Roo.isIE7){
6484             this.autoWidth.defer(1, this);
6485         }else{
6486             this.autoWidth();
6487         }
6488         if(this.handleMouseEvents){
6489             btn.on("mouseover", this.onMouseOver, this);
6490             btn.on("mouseout", this.onMouseOut, this);
6491             btn.on("mousedown", this.onMouseDown, this);
6492         }
6493         btn.on(this.clickEvent, this.onClick, this);
6494         //btn.on("mouseup", this.onMouseUp, this);
6495         if(this.hidden){
6496             this.hide();
6497         }
6498         if(this.disabled){
6499             this.disable();
6500         }
6501         Roo.ButtonToggleMgr.register(this);
6502         if(this.pressed){
6503             this.el.addClass("x-btn-pressed");
6504         }
6505         if(this.repeat){
6506             var repeater = new Roo.util.ClickRepeater(btn,
6507                 typeof this.repeat == "object" ? this.repeat : {}
6508             );
6509             repeater.on("click", this.onClick,  this);
6510         }
6511         
6512         this.fireEvent('render', this);
6513         
6514     },
6515     /**
6516      * Returns the button's underlying element
6517      * @return {Roo.Element} The element
6518      */
6519     getEl : function(){
6520         return this.el;  
6521     },
6522     
6523     /**
6524      * Destroys this Button and removes any listeners.
6525      */
6526     destroy : function(){
6527         Roo.ButtonToggleMgr.unregister(this);
6528         this.el.removeAllListeners();
6529         this.purgeListeners();
6530         this.el.remove();
6531     },
6532
6533     // private
6534     autoWidth : function(){
6535         if(this.el){
6536             this.el.setWidth("auto");
6537             if(Roo.isIE7 && Roo.isStrict){
6538                 var ib = this.el.child('button');
6539                 if(ib && ib.getWidth() > 20){
6540                     ib.clip();
6541                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6542                 }
6543             }
6544             if(this.minWidth){
6545                 if(this.hidden){
6546                     this.el.beginMeasure();
6547                 }
6548                 if(this.el.getWidth() < this.minWidth){
6549                     this.el.setWidth(this.minWidth);
6550                 }
6551                 if(this.hidden){
6552                     this.el.endMeasure();
6553                 }
6554             }
6555         }
6556     },
6557
6558     /**
6559      * Assigns this button's click handler
6560      * @param {Function} handler The function to call when the button is clicked
6561      * @param {Object} scope (optional) Scope for the function passed in
6562      */
6563     setHandler : function(handler, scope){
6564         this.handler = handler;
6565         this.scope = scope;  
6566     },
6567     
6568     /**
6569      * Sets this button's text
6570      * @param {String} text The button text
6571      */
6572     setText : function(text){
6573         this.text = text;
6574         if(this.el){
6575             this.el.child("td.x-btn-center button.x-btn-text").update(text);
6576         }
6577         this.autoWidth();
6578     },
6579     
6580     /**
6581      * Gets the text for this button
6582      * @return {String} The button text
6583      */
6584     getText : function(){
6585         return this.text;  
6586     },
6587     
6588     /**
6589      * Show this button
6590      */
6591     show: function(){
6592         this.hidden = false;
6593         if(this.el){
6594             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
6595         }
6596     },
6597     
6598     /**
6599      * Hide this button
6600      */
6601     hide: function(){
6602         this.hidden = true;
6603         if(this.el){
6604             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
6605         }
6606     },
6607     
6608     /**
6609      * Convenience function for boolean show/hide
6610      * @param {Boolean} visible True to show, false to hide
6611      */
6612     setVisible: function(visible){
6613         if(visible) {
6614             this.show();
6615         }else{
6616             this.hide();
6617         }
6618     },
6619     
6620     /**
6621      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
6622      * @param {Boolean} state (optional) Force a particular state
6623      */
6624     toggle : function(state){
6625         state = state === undefined ? !this.pressed : state;
6626         if(state != this.pressed){
6627             if(state){
6628                 this.el.addClass("x-btn-pressed");
6629                 this.pressed = true;
6630                 this.fireEvent("toggle", this, true);
6631             }else{
6632                 this.el.removeClass("x-btn-pressed");
6633                 this.pressed = false;
6634                 this.fireEvent("toggle", this, false);
6635             }
6636             if(this.toggleHandler){
6637                 this.toggleHandler.call(this.scope || this, this, state);
6638             }
6639         }
6640     },
6641     
6642     /**
6643      * Focus the button
6644      */
6645     focus : function(){
6646         this.el.child('button:first').focus();
6647     },
6648     
6649     /**
6650      * Disable this button
6651      */
6652     disable : function(){
6653         if(this.el){
6654             this.el.addClass("x-btn-disabled");
6655         }
6656         this.disabled = true;
6657     },
6658     
6659     /**
6660      * Enable this button
6661      */
6662     enable : function(){
6663         if(this.el){
6664             this.el.removeClass("x-btn-disabled");
6665         }
6666         this.disabled = false;
6667     },
6668
6669     /**
6670      * Convenience function for boolean enable/disable
6671      * @param {Boolean} enabled True to enable, false to disable
6672      */
6673     setDisabled : function(v){
6674         this[v !== true ? "enable" : "disable"]();
6675     },
6676
6677     // private
6678     onClick : function(e)
6679     {
6680         if(e){
6681             e.preventDefault();
6682         }
6683         if(e.button != 0){
6684             return;
6685         }
6686         if(!this.disabled){
6687             if(this.enableToggle){
6688                 this.toggle();
6689             }
6690             if(this.menu && !this.menu.isVisible()){
6691                 this.menu.show(this.el, this.menuAlign);
6692             }
6693             this.fireEvent("click", this, e);
6694             if(this.handler){
6695                 this.el.removeClass("x-btn-over");
6696                 this.handler.call(this.scope || this, this, e);
6697             }
6698         }
6699     },
6700     // private
6701     onMouseOver : function(e){
6702         if(!this.disabled){
6703             this.el.addClass("x-btn-over");
6704             this.fireEvent('mouseover', this, e);
6705         }
6706     },
6707     // private
6708     onMouseOut : function(e){
6709         if(!e.within(this.el,  true)){
6710             this.el.removeClass("x-btn-over");
6711             this.fireEvent('mouseout', this, e);
6712         }
6713     },
6714     // private
6715     onFocus : function(e){
6716         if(!this.disabled){
6717             this.el.addClass("x-btn-focus");
6718         }
6719     },
6720     // private
6721     onBlur : function(e){
6722         this.el.removeClass("x-btn-focus");
6723     },
6724     // private
6725     onMouseDown : function(e){
6726         if(!this.disabled && e.button == 0){
6727             this.el.addClass("x-btn-click");
6728             Roo.get(document).on('mouseup', this.onMouseUp, this);
6729         }
6730     },
6731     // private
6732     onMouseUp : function(e){
6733         if(e.button == 0){
6734             this.el.removeClass("x-btn-click");
6735             Roo.get(document).un('mouseup', this.onMouseUp, this);
6736         }
6737     },
6738     // private
6739     onMenuShow : function(e){
6740         this.el.addClass("x-btn-menu-active");
6741     },
6742     // private
6743     onMenuHide : function(e){
6744         this.el.removeClass("x-btn-menu-active");
6745     }   
6746 });
6747
6748 // Private utility class used by Button
6749 Roo.ButtonToggleMgr = function(){
6750    var groups = {};
6751    
6752    function toggleGroup(btn, state){
6753        if(state){
6754            var g = groups[btn.toggleGroup];
6755            for(var i = 0, l = g.length; i < l; i++){
6756                if(g[i] != btn){
6757                    g[i].toggle(false);
6758                }
6759            }
6760        }
6761    }
6762    
6763    return {
6764        register : function(btn){
6765            if(!btn.toggleGroup){
6766                return;
6767            }
6768            var g = groups[btn.toggleGroup];
6769            if(!g){
6770                g = groups[btn.toggleGroup] = [];
6771            }
6772            g.push(btn);
6773            btn.on("toggle", toggleGroup);
6774        },
6775        
6776        unregister : function(btn){
6777            if(!btn.toggleGroup){
6778                return;
6779            }
6780            var g = groups[btn.toggleGroup];
6781            if(g){
6782                g.remove(btn);
6783                btn.un("toggle", toggleGroup);
6784            }
6785        }
6786    };
6787 }();/*
6788  * Based on:
6789  * Ext JS Library 1.1.1
6790  * Copyright(c) 2006-2007, Ext JS, LLC.
6791  *
6792  * Originally Released Under LGPL - original licence link has changed is not relivant.
6793  *
6794  * Fork - LGPL
6795  * <script type="text/javascript">
6796  */
6797  
6798 /**
6799  * @class Roo.SplitButton
6800  * @extends Roo.Button
6801  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
6802  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
6803  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
6804  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
6805  * @cfg {String} arrowTooltip The title attribute of the arrow
6806  * @constructor
6807  * Create a new menu button
6808  * @param {String/HTMLElement/Element} renderTo The element to append the button to
6809  * @param {Object} config The config object
6810  */
6811 Roo.SplitButton = function(renderTo, config){
6812     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
6813     /**
6814      * @event arrowclick
6815      * Fires when this button's arrow is clicked
6816      * @param {SplitButton} this
6817      * @param {EventObject} e The click event
6818      */
6819     this.addEvents({"arrowclick":true});
6820 };
6821
6822 Roo.extend(Roo.SplitButton, Roo.Button, {
6823     render : function(renderTo){
6824         // this is one sweet looking template!
6825         var tpl = new Roo.Template(
6826             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
6827             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
6828             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
6829             "</tbody></table></td><td>",
6830             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
6831             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
6832             "</tbody></table></td></tr></table>"
6833         );
6834         var btn = tpl.append(renderTo, [this.text, this.type], true);
6835         var btnEl = btn.child("button");
6836         if(this.cls){
6837             btn.addClass(this.cls);
6838         }
6839         if(this.icon){
6840             btnEl.setStyle('background-image', 'url(' +this.icon +')');
6841         }
6842         if(this.iconCls){
6843             btnEl.addClass(this.iconCls);
6844             if(!this.cls){
6845                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
6846             }
6847         }
6848         this.el = btn;
6849         if(this.handleMouseEvents){
6850             btn.on("mouseover", this.onMouseOver, this);
6851             btn.on("mouseout", this.onMouseOut, this);
6852             btn.on("mousedown", this.onMouseDown, this);
6853             btn.on("mouseup", this.onMouseUp, this);
6854         }
6855         btn.on(this.clickEvent, this.onClick, this);
6856         if(this.tooltip){
6857             if(typeof this.tooltip == 'object'){
6858                 Roo.QuickTips.tips(Roo.apply({
6859                       target: btnEl.id
6860                 }, this.tooltip));
6861             } else {
6862                 btnEl.dom[this.tooltipType] = this.tooltip;
6863             }
6864         }
6865         if(this.arrowTooltip){
6866             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
6867         }
6868         if(this.hidden){
6869             this.hide();
6870         }
6871         if(this.disabled){
6872             this.disable();
6873         }
6874         if(this.pressed){
6875             this.el.addClass("x-btn-pressed");
6876         }
6877         if(Roo.isIE && !Roo.isIE7){
6878             this.autoWidth.defer(1, this);
6879         }else{
6880             this.autoWidth();
6881         }
6882         if(this.menu){
6883             this.menu.on("show", this.onMenuShow, this);
6884             this.menu.on("hide", this.onMenuHide, this);
6885         }
6886         this.fireEvent('render', this);
6887     },
6888
6889     // private
6890     autoWidth : function(){
6891         if(this.el){
6892             var tbl = this.el.child("table:first");
6893             var tbl2 = this.el.child("table:last");
6894             this.el.setWidth("auto");
6895             tbl.setWidth("auto");
6896             if(Roo.isIE7 && Roo.isStrict){
6897                 var ib = this.el.child('button:first');
6898                 if(ib && ib.getWidth() > 20){
6899                     ib.clip();
6900                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
6901                 }
6902             }
6903             if(this.minWidth){
6904                 if(this.hidden){
6905                     this.el.beginMeasure();
6906                 }
6907                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
6908                     tbl.setWidth(this.minWidth-tbl2.getWidth());
6909                 }
6910                 if(this.hidden){
6911                     this.el.endMeasure();
6912                 }
6913             }
6914             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
6915         } 
6916     },
6917     /**
6918      * Sets this button's click handler
6919      * @param {Function} handler The function to call when the button is clicked
6920      * @param {Object} scope (optional) Scope for the function passed above
6921      */
6922     setHandler : function(handler, scope){
6923         this.handler = handler;
6924         this.scope = scope;  
6925     },
6926     
6927     /**
6928      * Sets this button's arrow click handler
6929      * @param {Function} handler The function to call when the arrow is clicked
6930      * @param {Object} scope (optional) Scope for the function passed above
6931      */
6932     setArrowHandler : function(handler, scope){
6933         this.arrowHandler = handler;
6934         this.scope = scope;  
6935     },
6936     
6937     /**
6938      * Focus the button
6939      */
6940     focus : function(){
6941         if(this.el){
6942             this.el.child("button:first").focus();
6943         }
6944     },
6945
6946     // private
6947     onClick : function(e){
6948         e.preventDefault();
6949         if(!this.disabled){
6950             if(e.getTarget(".x-btn-menu-arrow-wrap")){
6951                 if(this.menu && !this.menu.isVisible()){
6952                     this.menu.show(this.el, this.menuAlign);
6953                 }
6954                 this.fireEvent("arrowclick", this, e);
6955                 if(this.arrowHandler){
6956                     this.arrowHandler.call(this.scope || this, this, e);
6957                 }
6958             }else{
6959                 this.fireEvent("click", this, e);
6960                 if(this.handler){
6961                     this.handler.call(this.scope || this, this, e);
6962                 }
6963             }
6964         }
6965     },
6966     // private
6967     onMouseDown : function(e){
6968         if(!this.disabled){
6969             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
6970         }
6971     },
6972     // private
6973     onMouseUp : function(e){
6974         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
6975     }   
6976 });
6977
6978
6979 // backwards compat
6980 Roo.MenuButton = Roo.SplitButton;/*
6981  * Based on:
6982  * Ext JS Library 1.1.1
6983  * Copyright(c) 2006-2007, Ext JS, LLC.
6984  *
6985  * Originally Released Under LGPL - original licence link has changed is not relivant.
6986  *
6987  * Fork - LGPL
6988  * <script type="text/javascript">
6989  */
6990
6991 /**
6992  * @class Roo.Toolbar
6993  * Basic Toolbar class.
6994  * @constructor
6995  * Creates a new Toolbar
6996  * @param {Object} container The config object
6997  */ 
6998 Roo.Toolbar = function(container, buttons, config)
6999 {
7000     /// old consturctor format still supported..
7001     if(container instanceof Array){ // omit the container for later rendering
7002         buttons = container;
7003         config = buttons;
7004         container = null;
7005     }
7006     if (typeof(container) == 'object' && container.xtype) {
7007         config = container;
7008         container = config.container;
7009         buttons = config.buttons || []; // not really - use items!!
7010     }
7011     var xitems = [];
7012     if (config && config.items) {
7013         xitems = config.items;
7014         delete config.items;
7015     }
7016     Roo.apply(this, config);
7017     this.buttons = buttons;
7018     
7019     if(container){
7020         this.render(container);
7021     }
7022     this.xitems = xitems;
7023     Roo.each(xitems, function(b) {
7024         this.add(b);
7025     }, this);
7026     
7027 };
7028
7029 Roo.Toolbar.prototype = {
7030     /**
7031      * @cfg {Array} items
7032      * array of button configs or elements to add (will be converted to a MixedCollection)
7033      */
7034     
7035     /**
7036      * @cfg {String/HTMLElement/Element} container
7037      * The id or element that will contain the toolbar
7038      */
7039     // private
7040     render : function(ct){
7041         this.el = Roo.get(ct);
7042         if(this.cls){
7043             this.el.addClass(this.cls);
7044         }
7045         // using a table allows for vertical alignment
7046         // 100% width is needed by Safari...
7047         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
7048         this.tr = this.el.child("tr", true);
7049         var autoId = 0;
7050         this.items = new Roo.util.MixedCollection(false, function(o){
7051             return o.id || ("item" + (++autoId));
7052         });
7053         if(this.buttons){
7054             this.add.apply(this, this.buttons);
7055             delete this.buttons;
7056         }
7057     },
7058
7059     /**
7060      * Adds element(s) to the toolbar -- this function takes a variable number of 
7061      * arguments of mixed type and adds them to the toolbar.
7062      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
7063      * <ul>
7064      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
7065      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
7066      * <li>Field: Any form field (equivalent to {@link #addField})</li>
7067      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
7068      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
7069      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
7070      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
7071      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
7072      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
7073      * </ul>
7074      * @param {Mixed} arg2
7075      * @param {Mixed} etc.
7076      */
7077     add : function(){
7078         var a = arguments, l = a.length;
7079         for(var i = 0; i < l; i++){
7080             this._add(a[i]);
7081         }
7082     },
7083     // private..
7084     _add : function(el) {
7085         
7086         if (el.xtype) {
7087             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
7088         }
7089         
7090         if (el.applyTo){ // some kind of form field
7091             return this.addField(el);
7092         } 
7093         if (el.render){ // some kind of Toolbar.Item
7094             return this.addItem(el);
7095         }
7096         if (typeof el == "string"){ // string
7097             if(el == "separator" || el == "-"){
7098                 return this.addSeparator();
7099             }
7100             if (el == " "){
7101                 return this.addSpacer();
7102             }
7103             if(el == "->"){
7104                 return this.addFill();
7105             }
7106             return this.addText(el);
7107             
7108         }
7109         if(el.tagName){ // element
7110             return this.addElement(el);
7111         }
7112         if(typeof el == "object"){ // must be button config?
7113             return this.addButton(el);
7114         }
7115         // and now what?!?!
7116         return false;
7117         
7118     },
7119     
7120     /**
7121      * Add an Xtype element
7122      * @param {Object} xtype Xtype Object
7123      * @return {Object} created Object
7124      */
7125     addxtype : function(e){
7126         return this.add(e);  
7127     },
7128     
7129     /**
7130      * Returns the Element for this toolbar.
7131      * @return {Roo.Element}
7132      */
7133     getEl : function(){
7134         return this.el;  
7135     },
7136     
7137     /**
7138      * Adds a separator
7139      * @return {Roo.Toolbar.Item} The separator item
7140      */
7141     addSeparator : function(){
7142         return this.addItem(new Roo.Toolbar.Separator());
7143     },
7144
7145     /**
7146      * Adds a spacer element
7147      * @return {Roo.Toolbar.Spacer} The spacer item
7148      */
7149     addSpacer : function(){
7150         return this.addItem(new Roo.Toolbar.Spacer());
7151     },
7152
7153     /**
7154      * Adds a fill element that forces subsequent additions to the right side of the toolbar
7155      * @return {Roo.Toolbar.Fill} The fill item
7156      */
7157     addFill : function(){
7158         return this.addItem(new Roo.Toolbar.Fill());
7159     },
7160
7161     /**
7162      * Adds any standard HTML element to the toolbar
7163      * @param {String/HTMLElement/Element} el The element or id of the element to add
7164      * @return {Roo.Toolbar.Item} The element's item
7165      */
7166     addElement : function(el){
7167         return this.addItem(new Roo.Toolbar.Item(el));
7168     },
7169     /**
7170      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
7171      * @type Roo.util.MixedCollection  
7172      */
7173     items : false,
7174      
7175     /**
7176      * Adds any Toolbar.Item or subclass
7177      * @param {Roo.Toolbar.Item} item
7178      * @return {Roo.Toolbar.Item} The item
7179      */
7180     addItem : function(item){
7181         var td = this.nextBlock();
7182         item.render(td);
7183         this.items.add(item);
7184         return item;
7185     },
7186     
7187     /**
7188      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
7189      * @param {Object/Array} config A button config or array of configs
7190      * @return {Roo.Toolbar.Button/Array}
7191      */
7192     addButton : function(config){
7193         if(config instanceof Array){
7194             var buttons = [];
7195             for(var i = 0, len = config.length; i < len; i++) {
7196                 buttons.push(this.addButton(config[i]));
7197             }
7198             return buttons;
7199         }
7200         var b = config;
7201         if(!(config instanceof Roo.Toolbar.Button)){
7202             b = config.split ?
7203                 new Roo.Toolbar.SplitButton(config) :
7204                 new Roo.Toolbar.Button(config);
7205         }
7206         var td = this.nextBlock();
7207         b.render(td);
7208         this.items.add(b);
7209         return b;
7210     },
7211     
7212     /**
7213      * Adds text to the toolbar
7214      * @param {String} text The text to add
7215      * @return {Roo.Toolbar.Item} The element's item
7216      */
7217     addText : function(text){
7218         return this.addItem(new Roo.Toolbar.TextItem(text));
7219     },
7220     
7221     /**
7222      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
7223      * @param {Number} index The index where the item is to be inserted
7224      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
7225      * @return {Roo.Toolbar.Button/Item}
7226      */
7227     insertButton : function(index, item){
7228         if(item instanceof Array){
7229             var buttons = [];
7230             for(var i = 0, len = item.length; i < len; i++) {
7231                buttons.push(this.insertButton(index + i, item[i]));
7232             }
7233             return buttons;
7234         }
7235         if (!(item instanceof Roo.Toolbar.Button)){
7236            item = new Roo.Toolbar.Button(item);
7237         }
7238         var td = document.createElement("td");
7239         this.tr.insertBefore(td, this.tr.childNodes[index]);
7240         item.render(td);
7241         this.items.insert(index, item);
7242         return item;
7243     },
7244     
7245     /**
7246      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
7247      * @param {Object} config
7248      * @return {Roo.Toolbar.Item} The element's item
7249      */
7250     addDom : function(config, returnEl){
7251         var td = this.nextBlock();
7252         Roo.DomHelper.overwrite(td, config);
7253         var ti = new Roo.Toolbar.Item(td.firstChild);
7254         ti.render(td);
7255         this.items.add(ti);
7256         return ti;
7257     },
7258
7259     /**
7260      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
7261      * @type Roo.util.MixedCollection  
7262      */
7263     fields : false,
7264     
7265     /**
7266      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
7267      * Note: the field should not have been rendered yet. For a field that has already been
7268      * rendered, use {@link #addElement}.
7269      * @param {Roo.form.Field} field
7270      * @return {Roo.ToolbarItem}
7271      */
7272      
7273       
7274     addField : function(field) {
7275         if (!this.fields) {
7276             var autoId = 0;
7277             this.fields = new Roo.util.MixedCollection(false, function(o){
7278                 return o.id || ("item" + (++autoId));
7279             });
7280
7281         }
7282         
7283         var td = this.nextBlock();
7284         field.render(td);
7285         var ti = new Roo.Toolbar.Item(td.firstChild);
7286         ti.render(td);
7287         this.items.add(ti);
7288         this.fields.add(field);
7289         return ti;
7290     },
7291     /**
7292      * Hide the toolbar
7293      * @method hide
7294      */
7295      
7296       
7297     hide : function()
7298     {
7299         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
7300         this.el.child('div').hide();
7301     },
7302     /**
7303      * Show the toolbar
7304      * @method show
7305      */
7306     show : function()
7307     {
7308         this.el.child('div').show();
7309     },
7310       
7311     // private
7312     nextBlock : function(){
7313         var td = document.createElement("td");
7314         this.tr.appendChild(td);
7315         return td;
7316     },
7317
7318     // private
7319     destroy : function(){
7320         if(this.items){ // rendered?
7321             Roo.destroy.apply(Roo, this.items.items);
7322         }
7323         if(this.fields){ // rendered?
7324             Roo.destroy.apply(Roo, this.fields.items);
7325         }
7326         Roo.Element.uncache(this.el, this.tr);
7327     }
7328 };
7329
7330 /**
7331  * @class Roo.Toolbar.Item
7332  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
7333  * @constructor
7334  * Creates a new Item
7335  * @param {HTMLElement} el 
7336  */
7337 Roo.Toolbar.Item = function(el){
7338     var cfg = {};
7339     if (typeof (el.xtype) != 'undefined') {
7340         cfg = el;
7341         el = cfg.el;
7342     }
7343     
7344     this.el = Roo.getDom(el);
7345     this.id = Roo.id(this.el);
7346     this.hidden = false;
7347     
7348     this.addEvents({
7349          /**
7350              * @event render
7351              * Fires when the button is rendered
7352              * @param {Button} this
7353              */
7354         'render': true
7355     });
7356     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
7357 };
7358 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
7359 //Roo.Toolbar.Item.prototype = {
7360     
7361     /**
7362      * Get this item's HTML Element
7363      * @return {HTMLElement}
7364      */
7365     getEl : function(){
7366        return this.el;  
7367     },
7368
7369     // private
7370     render : function(td){
7371         
7372          this.td = td;
7373         td.appendChild(this.el);
7374         
7375         this.fireEvent('render', this);
7376     },
7377     
7378     /**
7379      * Removes and destroys this item.
7380      */
7381     destroy : function(){
7382         this.td.parentNode.removeChild(this.td);
7383     },
7384     
7385     /**
7386      * Shows this item.
7387      */
7388     show: function(){
7389         this.hidden = false;
7390         this.td.style.display = "";
7391     },
7392     
7393     /**
7394      * Hides this item.
7395      */
7396     hide: function(){
7397         this.hidden = true;
7398         this.td.style.display = "none";
7399     },
7400     
7401     /**
7402      * Convenience function for boolean show/hide.
7403      * @param {Boolean} visible true to show/false to hide
7404      */
7405     setVisible: function(visible){
7406         if(visible) {
7407             this.show();
7408         }else{
7409             this.hide();
7410         }
7411     },
7412     
7413     /**
7414      * Try to focus this item.
7415      */
7416     focus : function(){
7417         Roo.fly(this.el).focus();
7418     },
7419     
7420     /**
7421      * Disables this item.
7422      */
7423     disable : function(){
7424         Roo.fly(this.td).addClass("x-item-disabled");
7425         this.disabled = true;
7426         this.el.disabled = true;
7427     },
7428     
7429     /**
7430      * Enables this item.
7431      */
7432     enable : function(){
7433         Roo.fly(this.td).removeClass("x-item-disabled");
7434         this.disabled = false;
7435         this.el.disabled = false;
7436     }
7437 });
7438
7439
7440 /**
7441  * @class Roo.Toolbar.Separator
7442  * @extends Roo.Toolbar.Item
7443  * A simple toolbar separator class
7444  * @constructor
7445  * Creates a new Separator
7446  */
7447 Roo.Toolbar.Separator = function(cfg){
7448     
7449     var s = document.createElement("span");
7450     s.className = "ytb-sep";
7451     if (cfg) {
7452         cfg.el = s;
7453     }
7454     
7455     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
7456 };
7457 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
7458     enable:Roo.emptyFn,
7459     disable:Roo.emptyFn,
7460     focus:Roo.emptyFn
7461 });
7462
7463 /**
7464  * @class Roo.Toolbar.Spacer
7465  * @extends Roo.Toolbar.Item
7466  * A simple element that adds extra horizontal space to a toolbar.
7467  * @constructor
7468  * Creates a new Spacer
7469  */
7470 Roo.Toolbar.Spacer = function(cfg){
7471     var s = document.createElement("div");
7472     s.className = "ytb-spacer";
7473     if (cfg) {
7474         cfg.el = s;
7475     }
7476     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
7477 };
7478 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
7479     enable:Roo.emptyFn,
7480     disable:Roo.emptyFn,
7481     focus:Roo.emptyFn
7482 });
7483
7484 /**
7485  * @class Roo.Toolbar.Fill
7486  * @extends Roo.Toolbar.Spacer
7487  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
7488  * @constructor
7489  * Creates a new Spacer
7490  */
7491 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
7492     // private
7493     render : function(td){
7494         td.style.width = '100%';
7495         Roo.Toolbar.Fill.superclass.render.call(this, td);
7496     }
7497 });
7498
7499 /**
7500  * @class Roo.Toolbar.TextItem
7501  * @extends Roo.Toolbar.Item
7502  * A simple class that renders text directly into a toolbar.
7503  * @constructor
7504  * Creates a new TextItem
7505  * @cfg {string} text 
7506  */
7507 Roo.Toolbar.TextItem = function(cfg){
7508     var  text = cfg || "";
7509     if (typeof(cfg) == 'object') {
7510         text = cfg.text || "";
7511     }  else {
7512         cfg = null;
7513     }
7514     var s = document.createElement("span");
7515     s.className = "ytb-text";
7516     s.innerHTML = text;
7517     if (cfg) {
7518         cfg.el  = s;
7519     }
7520     
7521     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
7522 };
7523 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
7524     
7525      
7526     enable:Roo.emptyFn,
7527     disable:Roo.emptyFn,
7528     focus:Roo.emptyFn
7529 });
7530
7531 /**
7532  * @class Roo.Toolbar.Button
7533  * @extends Roo.Button
7534  * A button that renders into a toolbar.
7535  * @constructor
7536  * Creates a new Button
7537  * @param {Object} config A standard {@link Roo.Button} config object
7538  */
7539 Roo.Toolbar.Button = function(config){
7540     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
7541 };
7542 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
7543     render : function(td){
7544         this.td = td;
7545         Roo.Toolbar.Button.superclass.render.call(this, td);
7546     },
7547     
7548     /**
7549      * Removes and destroys this button
7550      */
7551     destroy : function(){
7552         Roo.Toolbar.Button.superclass.destroy.call(this);
7553         this.td.parentNode.removeChild(this.td);
7554     },
7555     
7556     /**
7557      * Shows this button
7558      */
7559     show: function(){
7560         this.hidden = false;
7561         this.td.style.display = "";
7562     },
7563     
7564     /**
7565      * Hides this button
7566      */
7567     hide: function(){
7568         this.hidden = true;
7569         this.td.style.display = "none";
7570     },
7571
7572     /**
7573      * Disables this item
7574      */
7575     disable : function(){
7576         Roo.fly(this.td).addClass("x-item-disabled");
7577         this.disabled = true;
7578     },
7579
7580     /**
7581      * Enables this item
7582      */
7583     enable : function(){
7584         Roo.fly(this.td).removeClass("x-item-disabled");
7585         this.disabled = false;
7586     }
7587 });
7588 // backwards compat
7589 Roo.ToolbarButton = Roo.Toolbar.Button;
7590
7591 /**
7592  * @class Roo.Toolbar.SplitButton
7593  * @extends Roo.SplitButton
7594  * A menu button that renders into a toolbar.
7595  * @constructor
7596  * Creates a new SplitButton
7597  * @param {Object} config A standard {@link Roo.SplitButton} config object
7598  */
7599 Roo.Toolbar.SplitButton = function(config){
7600     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
7601 };
7602 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
7603     render : function(td){
7604         this.td = td;
7605         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
7606     },
7607     
7608     /**
7609      * Removes and destroys this button
7610      */
7611     destroy : function(){
7612         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
7613         this.td.parentNode.removeChild(this.td);
7614     },
7615     
7616     /**
7617      * Shows this button
7618      */
7619     show: function(){
7620         this.hidden = false;
7621         this.td.style.display = "";
7622     },
7623     
7624     /**
7625      * Hides this button
7626      */
7627     hide: function(){
7628         this.hidden = true;
7629         this.td.style.display = "none";
7630     }
7631 });
7632
7633 // backwards compat
7634 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
7635  * Based on:
7636  * Ext JS Library 1.1.1
7637  * Copyright(c) 2006-2007, Ext JS, LLC.
7638  *
7639  * Originally Released Under LGPL - original licence link has changed is not relivant.
7640  *
7641  * Fork - LGPL
7642  * <script type="text/javascript">
7643  */
7644  
7645 /**
7646  * @class Roo.PagingToolbar
7647  * @extends Roo.Toolbar
7648  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
7649  * @constructor
7650  * Create a new PagingToolbar
7651  * @param {Object} config The config object
7652  */
7653 Roo.PagingToolbar = function(el, ds, config)
7654 {
7655     // old args format still supported... - xtype is prefered..
7656     if (typeof(el) == 'object' && el.xtype) {
7657         // created from xtype...
7658         config = el;
7659         ds = el.dataSource;
7660         el = config.container;
7661     }
7662     var items = [];
7663     if (config.items) {
7664         items = config.items;
7665         config.items = [];
7666     }
7667     
7668     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
7669     this.ds = ds;
7670     this.cursor = 0;
7671     this.renderButtons(this.el);
7672     this.bind(ds);
7673     
7674     // supprot items array.
7675    
7676     Roo.each(items, function(e) {
7677         this.add(Roo.factory(e));
7678     },this);
7679     
7680 };
7681
7682 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
7683     /**
7684      * @cfg {Roo.data.Store} dataSource
7685      * The underlying data store providing the paged data
7686      */
7687     /**
7688      * @cfg {String/HTMLElement/Element} container
7689      * container The id or element that will contain the toolbar
7690      */
7691     /**
7692      * @cfg {Boolean} displayInfo
7693      * True to display the displayMsg (defaults to false)
7694      */
7695     /**
7696      * @cfg {Number} pageSize
7697      * The number of records to display per page (defaults to 20)
7698      */
7699     pageSize: 20,
7700     /**
7701      * @cfg {String} displayMsg
7702      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
7703      */
7704     displayMsg : 'Displaying {0} - {1} of {2}',
7705     /**
7706      * @cfg {String} emptyMsg
7707      * The message to display when no records are found (defaults to "No data to display")
7708      */
7709     emptyMsg : 'No data to display',
7710     /**
7711      * Customizable piece of the default paging text (defaults to "Page")
7712      * @type String
7713      */
7714     beforePageText : "Page",
7715     /**
7716      * Customizable piece of the default paging text (defaults to "of %0")
7717      * @type String
7718      */
7719     afterPageText : "of {0}",
7720     /**
7721      * Customizable piece of the default paging text (defaults to "First Page")
7722      * @type String
7723      */
7724     firstText : "First Page",
7725     /**
7726      * Customizable piece of the default paging text (defaults to "Previous Page")
7727      * @type String
7728      */
7729     prevText : "Previous Page",
7730     /**
7731      * Customizable piece of the default paging text (defaults to "Next Page")
7732      * @type String
7733      */
7734     nextText : "Next Page",
7735     /**
7736      * Customizable piece of the default paging text (defaults to "Last Page")
7737      * @type String
7738      */
7739     lastText : "Last Page",
7740     /**
7741      * Customizable piece of the default paging text (defaults to "Refresh")
7742      * @type String
7743      */
7744     refreshText : "Refresh",
7745
7746     // private
7747     renderButtons : function(el){
7748         Roo.PagingToolbar.superclass.render.call(this, el);
7749         this.first = this.addButton({
7750             tooltip: this.firstText,
7751             cls: "x-btn-icon x-grid-page-first",
7752             disabled: true,
7753             handler: this.onClick.createDelegate(this, ["first"])
7754         });
7755         this.prev = this.addButton({
7756             tooltip: this.prevText,
7757             cls: "x-btn-icon x-grid-page-prev",
7758             disabled: true,
7759             handler: this.onClick.createDelegate(this, ["prev"])
7760         });
7761         //this.addSeparator();
7762         this.add(this.beforePageText);
7763         this.field = Roo.get(this.addDom({
7764            tag: "input",
7765            type: "text",
7766            size: "3",
7767            value: "1",
7768            cls: "x-grid-page-number"
7769         }).el);
7770         this.field.on("keydown", this.onPagingKeydown, this);
7771         this.field.on("focus", function(){this.dom.select();});
7772         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
7773         this.field.setHeight(18);
7774         //this.addSeparator();
7775         this.next = this.addButton({
7776             tooltip: this.nextText,
7777             cls: "x-btn-icon x-grid-page-next",
7778             disabled: true,
7779             handler: this.onClick.createDelegate(this, ["next"])
7780         });
7781         this.last = this.addButton({
7782             tooltip: this.lastText,
7783             cls: "x-btn-icon x-grid-page-last",
7784             disabled: true,
7785             handler: this.onClick.createDelegate(this, ["last"])
7786         });
7787         //this.addSeparator();
7788         this.loading = this.addButton({
7789             tooltip: this.refreshText,
7790             cls: "x-btn-icon x-grid-loading",
7791             handler: this.onClick.createDelegate(this, ["refresh"])
7792         });
7793
7794         if(this.displayInfo){
7795             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
7796         }
7797     },
7798
7799     // private
7800     updateInfo : function(){
7801         if(this.displayEl){
7802             var count = this.ds.getCount();
7803             var msg = count == 0 ?
7804                 this.emptyMsg :
7805                 String.format(
7806                     this.displayMsg,
7807                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
7808                 );
7809             this.displayEl.update(msg);
7810         }
7811     },
7812
7813     // private
7814     onLoad : function(ds, r, o){
7815        this.cursor = o.params ? o.params.start : 0;
7816        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
7817
7818        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
7819        this.field.dom.value = ap;
7820        this.first.setDisabled(ap == 1);
7821        this.prev.setDisabled(ap == 1);
7822        this.next.setDisabled(ap == ps);
7823        this.last.setDisabled(ap == ps);
7824        this.loading.enable();
7825        this.updateInfo();
7826     },
7827
7828     // private
7829     getPageData : function(){
7830         var total = this.ds.getTotalCount();
7831         return {
7832             total : total,
7833             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
7834             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
7835         };
7836     },
7837
7838     // private
7839     onLoadError : function(){
7840         this.loading.enable();
7841     },
7842
7843     // private
7844     onPagingKeydown : function(e){
7845         var k = e.getKey();
7846         var d = this.getPageData();
7847         if(k == e.RETURN){
7848             var v = this.field.dom.value, pageNum;
7849             if(!v || isNaN(pageNum = parseInt(v, 10))){
7850                 this.field.dom.value = d.activePage;
7851                 return;
7852             }
7853             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
7854             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7855             e.stopEvent();
7856         }
7857         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
7858         {
7859           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
7860           this.field.dom.value = pageNum;
7861           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
7862           e.stopEvent();
7863         }
7864         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
7865         {
7866           var v = this.field.dom.value, pageNum; 
7867           var increment = (e.shiftKey) ? 10 : 1;
7868           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
7869             increment *= -1;
7870           }
7871           if(!v || isNaN(pageNum = parseInt(v, 10))) {
7872             this.field.dom.value = d.activePage;
7873             return;
7874           }
7875           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
7876           {
7877             this.field.dom.value = parseInt(v, 10) + increment;
7878             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
7879             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
7880           }
7881           e.stopEvent();
7882         }
7883     },
7884
7885     // private
7886     beforeLoad : function(){
7887         if(this.loading){
7888             this.loading.disable();
7889         }
7890     },
7891
7892     // private
7893     onClick : function(which){
7894         var ds = this.ds;
7895         switch(which){
7896             case "first":
7897                 ds.load({params:{start: 0, limit: this.pageSize}});
7898             break;
7899             case "prev":
7900                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
7901             break;
7902             case "next":
7903                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
7904             break;
7905             case "last":
7906                 var total = ds.getTotalCount();
7907                 var extra = total % this.pageSize;
7908                 var lastStart = extra ? (total - extra) : total-this.pageSize;
7909                 ds.load({params:{start: lastStart, limit: this.pageSize}});
7910             break;
7911             case "refresh":
7912                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
7913             break;
7914         }
7915     },
7916
7917     /**
7918      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
7919      * @param {Roo.data.Store} store The data store to unbind
7920      */
7921     unbind : function(ds){
7922         ds.un("beforeload", this.beforeLoad, this);
7923         ds.un("load", this.onLoad, this);
7924         ds.un("loadexception", this.onLoadError, this);
7925         ds.un("remove", this.updateInfo, this);
7926         ds.un("add", this.updateInfo, this);
7927         this.ds = undefined;
7928     },
7929
7930     /**
7931      * Binds the paging toolbar to the specified {@link Roo.data.Store}
7932      * @param {Roo.data.Store} store The data store to bind
7933      */
7934     bind : function(ds){
7935         ds.on("beforeload", this.beforeLoad, this);
7936         ds.on("load", this.onLoad, this);
7937         ds.on("loadexception", this.onLoadError, this);
7938         ds.on("remove", this.updateInfo, this);
7939         ds.on("add", this.updateInfo, this);
7940         this.ds = ds;
7941     }
7942 });/*
7943  * Based on:
7944  * Ext JS Library 1.1.1
7945  * Copyright(c) 2006-2007, Ext JS, LLC.
7946  *
7947  * Originally Released Under LGPL - original licence link has changed is not relivant.
7948  *
7949  * Fork - LGPL
7950  * <script type="text/javascript">
7951  */
7952
7953 /**
7954  * @class Roo.Resizable
7955  * @extends Roo.util.Observable
7956  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
7957  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
7958  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
7959  * the element will be wrapped for you automatically.</p>
7960  * <p>Here is the list of valid resize handles:</p>
7961  * <pre>
7962 Value   Description
7963 ------  -------------------
7964  'n'     north
7965  's'     south
7966  'e'     east
7967  'w'     west
7968  'nw'    northwest
7969  'sw'    southwest
7970  'se'    southeast
7971  'ne'    northeast
7972  'hd'    horizontal drag
7973  'all'   all
7974 </pre>
7975  * <p>Here's an example showing the creation of a typical Resizable:</p>
7976  * <pre><code>
7977 var resizer = new Roo.Resizable("element-id", {
7978     handles: 'all',
7979     minWidth: 200,
7980     minHeight: 100,
7981     maxWidth: 500,
7982     maxHeight: 400,
7983     pinned: true
7984 });
7985 resizer.on("resize", myHandler);
7986 </code></pre>
7987  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
7988  * resizer.east.setDisplayed(false);</p>
7989  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
7990  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
7991  * resize operation's new size (defaults to [0, 0])
7992  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
7993  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
7994  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
7995  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
7996  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
7997  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
7998  * @cfg {Number} width The width of the element in pixels (defaults to null)
7999  * @cfg {Number} height The height of the element in pixels (defaults to null)
8000  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
8001  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
8002  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
8003  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
8004  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
8005  * in favor of the handles config option (defaults to false)
8006  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
8007  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
8008  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
8009  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
8010  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
8011  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
8012  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
8013  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
8014  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
8015  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
8016  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
8017  * @constructor
8018  * Create a new resizable component
8019  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
8020  * @param {Object} config configuration options
8021   */
8022 Roo.Resizable = function(el, config)
8023 {
8024     this.el = Roo.get(el);
8025
8026     if(config && config.wrap){
8027         config.resizeChild = this.el;
8028         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
8029         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
8030         this.el.setStyle("overflow", "hidden");
8031         this.el.setPositioning(config.resizeChild.getPositioning());
8032         config.resizeChild.clearPositioning();
8033         if(!config.width || !config.height){
8034             var csize = config.resizeChild.getSize();
8035             this.el.setSize(csize.width, csize.height);
8036         }
8037         if(config.pinned && !config.adjustments){
8038             config.adjustments = "auto";
8039         }
8040     }
8041
8042     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
8043     this.proxy.unselectable();
8044     this.proxy.enableDisplayMode('block');
8045
8046     Roo.apply(this, config);
8047
8048     if(this.pinned){
8049         this.disableTrackOver = true;
8050         this.el.addClass("x-resizable-pinned");
8051     }
8052     // if the element isn't positioned, make it relative
8053     var position = this.el.getStyle("position");
8054     if(position != "absolute" && position != "fixed"){
8055         this.el.setStyle("position", "relative");
8056     }
8057     if(!this.handles){ // no handles passed, must be legacy style
8058         this.handles = 's,e,se';
8059         if(this.multiDirectional){
8060             this.handles += ',n,w';
8061         }
8062     }
8063     if(this.handles == "all"){
8064         this.handles = "n s e w ne nw se sw";
8065     }
8066     var hs = this.handles.split(/\s*?[,;]\s*?| /);
8067     var ps = Roo.Resizable.positions;
8068     for(var i = 0, len = hs.length; i < len; i++){
8069         if(hs[i] && ps[hs[i]]){
8070             var pos = ps[hs[i]];
8071             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
8072         }
8073     }
8074     // legacy
8075     this.corner = this.southeast;
8076     
8077     // updateBox = the box can move..
8078     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
8079         this.updateBox = true;
8080     }
8081
8082     this.activeHandle = null;
8083
8084     if(this.resizeChild){
8085         if(typeof this.resizeChild == "boolean"){
8086             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
8087         }else{
8088             this.resizeChild = Roo.get(this.resizeChild, true);
8089         }
8090     }
8091     
8092     if(this.adjustments == "auto"){
8093         var rc = this.resizeChild;
8094         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
8095         if(rc && (hw || hn)){
8096             rc.position("relative");
8097             rc.setLeft(hw ? hw.el.getWidth() : 0);
8098             rc.setTop(hn ? hn.el.getHeight() : 0);
8099         }
8100         this.adjustments = [
8101             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
8102             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
8103         ];
8104     }
8105
8106     if(this.draggable){
8107         this.dd = this.dynamic ?
8108             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
8109         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
8110     }
8111
8112     // public events
8113     this.addEvents({
8114         /**
8115          * @event beforeresize
8116          * Fired before resize is allowed. Set enabled to false to cancel resize.
8117          * @param {Roo.Resizable} this
8118          * @param {Roo.EventObject} e The mousedown event
8119          */
8120         "beforeresize" : true,
8121         /**
8122          * @event resizing
8123          * Fired a resizing.
8124          * @param {Roo.Resizable} this
8125          * @param {Number} x The new x position
8126          * @param {Number} y The new y position
8127          * @param {Number} w The new w width
8128          * @param {Number} h The new h hight
8129          * @param {Roo.EventObject} e The mouseup event
8130          */
8131         "resizing" : true,
8132         /**
8133          * @event resize
8134          * Fired after a resize.
8135          * @param {Roo.Resizable} this
8136          * @param {Number} width The new width
8137          * @param {Number} height The new height
8138          * @param {Roo.EventObject} e The mouseup event
8139          */
8140         "resize" : true
8141     });
8142
8143     if(this.width !== null && this.height !== null){
8144         this.resizeTo(this.width, this.height);
8145     }else{
8146         this.updateChildSize();
8147     }
8148     if(Roo.isIE){
8149         this.el.dom.style.zoom = 1;
8150     }
8151     Roo.Resizable.superclass.constructor.call(this);
8152 };
8153
8154 Roo.extend(Roo.Resizable, Roo.util.Observable, {
8155         resizeChild : false,
8156         adjustments : [0, 0],
8157         minWidth : 5,
8158         minHeight : 5,
8159         maxWidth : 10000,
8160         maxHeight : 10000,
8161         enabled : true,
8162         animate : false,
8163         duration : .35,
8164         dynamic : false,
8165         handles : false,
8166         multiDirectional : false,
8167         disableTrackOver : false,
8168         easing : 'easeOutStrong',
8169         widthIncrement : 0,
8170         heightIncrement : 0,
8171         pinned : false,
8172         width : null,
8173         height : null,
8174         preserveRatio : false,
8175         transparent: false,
8176         minX: 0,
8177         minY: 0,
8178         draggable: false,
8179
8180         /**
8181          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
8182          */
8183         constrainTo: undefined,
8184         /**
8185          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
8186          */
8187         resizeRegion: undefined,
8188
8189
8190     /**
8191      * Perform a manual resize
8192      * @param {Number} width
8193      * @param {Number} height
8194      */
8195     resizeTo : function(width, height){
8196         this.el.setSize(width, height);
8197         this.updateChildSize();
8198         this.fireEvent("resize", this, width, height, null);
8199     },
8200
8201     // private
8202     startSizing : function(e, handle){
8203         this.fireEvent("beforeresize", this, e);
8204         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
8205
8206             if(!this.overlay){
8207                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
8208                 this.overlay.unselectable();
8209                 this.overlay.enableDisplayMode("block");
8210                 this.overlay.on("mousemove", this.onMouseMove, this);
8211                 this.overlay.on("mouseup", this.onMouseUp, this);
8212             }
8213             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
8214
8215             this.resizing = true;
8216             this.startBox = this.el.getBox();
8217             this.startPoint = e.getXY();
8218             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
8219                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
8220
8221             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
8222             this.overlay.show();
8223
8224             if(this.constrainTo) {
8225                 var ct = Roo.get(this.constrainTo);
8226                 this.resizeRegion = ct.getRegion().adjust(
8227                     ct.getFrameWidth('t'),
8228                     ct.getFrameWidth('l'),
8229                     -ct.getFrameWidth('b'),
8230                     -ct.getFrameWidth('r')
8231                 );
8232             }
8233
8234             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
8235             this.proxy.show();
8236             this.proxy.setBox(this.startBox);
8237             if(!this.dynamic){
8238                 this.proxy.setStyle('visibility', 'visible');
8239             }
8240         }
8241     },
8242
8243     // private
8244     onMouseDown : function(handle, e){
8245         if(this.enabled){
8246             e.stopEvent();
8247             this.activeHandle = handle;
8248             this.startSizing(e, handle);
8249         }
8250     },
8251
8252     // private
8253     onMouseUp : function(e){
8254         var size = this.resizeElement();
8255         this.resizing = false;
8256         this.handleOut();
8257         this.overlay.hide();
8258         this.proxy.hide();
8259         this.fireEvent("resize", this, size.width, size.height, e);
8260     },
8261
8262     // private
8263     updateChildSize : function(){
8264         
8265         if(this.resizeChild){
8266             var el = this.el;
8267             var child = this.resizeChild;
8268             var adj = this.adjustments;
8269             if(el.dom.offsetWidth){
8270                 var b = el.getSize(true);
8271                 child.setSize(b.width+adj[0], b.height+adj[1]);
8272             }
8273             // Second call here for IE
8274             // The first call enables instant resizing and
8275             // the second call corrects scroll bars if they
8276             // exist
8277             if(Roo.isIE){
8278                 setTimeout(function(){
8279                     if(el.dom.offsetWidth){
8280                         var b = el.getSize(true);
8281                         child.setSize(b.width+adj[0], b.height+adj[1]);
8282                     }
8283                 }, 10);
8284             }
8285         }
8286     },
8287
8288     // private
8289     snap : function(value, inc, min){
8290         if(!inc || !value) {
8291             return value;
8292         }
8293         var newValue = value;
8294         var m = value % inc;
8295         if(m > 0){
8296             if(m > (inc/2)){
8297                 newValue = value + (inc-m);
8298             }else{
8299                 newValue = value - m;
8300             }
8301         }
8302         return Math.max(min, newValue);
8303     },
8304
8305     // private
8306     resizeElement : function(){
8307         var box = this.proxy.getBox();
8308         if(this.updateBox){
8309             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
8310         }else{
8311             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
8312         }
8313         this.updateChildSize();
8314         if(!this.dynamic){
8315             this.proxy.hide();
8316         }
8317         return box;
8318     },
8319
8320     // private
8321     constrain : function(v, diff, m, mx){
8322         if(v - diff < m){
8323             diff = v - m;
8324         }else if(v - diff > mx){
8325             diff = mx - v;
8326         }
8327         return diff;
8328     },
8329
8330     // private
8331     onMouseMove : function(e){
8332         
8333         if(this.enabled){
8334             try{// try catch so if something goes wrong the user doesn't get hung
8335
8336             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
8337                 return;
8338             }
8339
8340             //var curXY = this.startPoint;
8341             var curSize = this.curSize || this.startBox;
8342             var x = this.startBox.x, y = this.startBox.y;
8343             var ox = x, oy = y;
8344             var w = curSize.width, h = curSize.height;
8345             var ow = w, oh = h;
8346             var mw = this.minWidth, mh = this.minHeight;
8347             var mxw = this.maxWidth, mxh = this.maxHeight;
8348             var wi = this.widthIncrement;
8349             var hi = this.heightIncrement;
8350
8351             var eventXY = e.getXY();
8352             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
8353             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
8354
8355             var pos = this.activeHandle.position;
8356
8357             switch(pos){
8358                 case "east":
8359                     w += diffX;
8360                     w = Math.min(Math.max(mw, w), mxw);
8361                     break;
8362              
8363                 case "south":
8364                     h += diffY;
8365                     h = Math.min(Math.max(mh, h), mxh);
8366                     break;
8367                 case "southeast":
8368                     w += diffX;
8369                     h += diffY;
8370                     w = Math.min(Math.max(mw, w), mxw);
8371                     h = Math.min(Math.max(mh, h), mxh);
8372                     break;
8373                 case "north":
8374                     diffY = this.constrain(h, diffY, mh, mxh);
8375                     y += diffY;
8376                     h -= diffY;
8377                     break;
8378                 case "hdrag":
8379                     
8380                     if (wi) {
8381                         var adiffX = Math.abs(diffX);
8382                         var sub = (adiffX % wi); // how much 
8383                         if (sub > (wi/2)) { // far enough to snap
8384                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
8385                         } else {
8386                             // remove difference.. 
8387                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
8388                         }
8389                     }
8390                     x += diffX;
8391                     x = Math.max(this.minX, x);
8392                     break;
8393                 case "west":
8394                     diffX = this.constrain(w, diffX, mw, mxw);
8395                     x += diffX;
8396                     w -= diffX;
8397                     break;
8398                 case "northeast":
8399                     w += diffX;
8400                     w = Math.min(Math.max(mw, w), mxw);
8401                     diffY = this.constrain(h, diffY, mh, mxh);
8402                     y += diffY;
8403                     h -= diffY;
8404                     break;
8405                 case "northwest":
8406                     diffX = this.constrain(w, diffX, mw, mxw);
8407                     diffY = this.constrain(h, diffY, mh, mxh);
8408                     y += diffY;
8409                     h -= diffY;
8410                     x += diffX;
8411                     w -= diffX;
8412                     break;
8413                case "southwest":
8414                     diffX = this.constrain(w, diffX, mw, mxw);
8415                     h += diffY;
8416                     h = Math.min(Math.max(mh, h), mxh);
8417                     x += diffX;
8418                     w -= diffX;
8419                     break;
8420             }
8421
8422             var sw = this.snap(w, wi, mw);
8423             var sh = this.snap(h, hi, mh);
8424             if(sw != w || sh != h){
8425                 switch(pos){
8426                     case "northeast":
8427                         y -= sh - h;
8428                     break;
8429                     case "north":
8430                         y -= sh - h;
8431                         break;
8432                     case "southwest":
8433                         x -= sw - w;
8434                     break;
8435                     case "west":
8436                         x -= sw - w;
8437                         break;
8438                     case "northwest":
8439                         x -= sw - w;
8440                         y -= sh - h;
8441                     break;
8442                 }
8443                 w = sw;
8444                 h = sh;
8445             }
8446
8447             if(this.preserveRatio){
8448                 switch(pos){
8449                     case "southeast":
8450                     case "east":
8451                         h = oh * (w/ow);
8452                         h = Math.min(Math.max(mh, h), mxh);
8453                         w = ow * (h/oh);
8454                        break;
8455                     case "south":
8456                         w = ow * (h/oh);
8457                         w = Math.min(Math.max(mw, w), mxw);
8458                         h = oh * (w/ow);
8459                         break;
8460                     case "northeast":
8461                         w = ow * (h/oh);
8462                         w = Math.min(Math.max(mw, w), mxw);
8463                         h = oh * (w/ow);
8464                     break;
8465                     case "north":
8466                         var tw = w;
8467                         w = ow * (h/oh);
8468                         w = Math.min(Math.max(mw, w), mxw);
8469                         h = oh * (w/ow);
8470                         x += (tw - w) / 2;
8471                         break;
8472                     case "southwest":
8473                         h = oh * (w/ow);
8474                         h = Math.min(Math.max(mh, h), mxh);
8475                         var tw = w;
8476                         w = ow * (h/oh);
8477                         x += tw - w;
8478                         break;
8479                     case "west":
8480                         var th = h;
8481                         h = oh * (w/ow);
8482                         h = Math.min(Math.max(mh, h), mxh);
8483                         y += (th - h) / 2;
8484                         var tw = w;
8485                         w = ow * (h/oh);
8486                         x += tw - w;
8487                        break;
8488                     case "northwest":
8489                         var tw = w;
8490                         var th = h;
8491                         h = oh * (w/ow);
8492                         h = Math.min(Math.max(mh, h), mxh);
8493                         w = ow * (h/oh);
8494                         y += th - h;
8495                         x += tw - w;
8496                        break;
8497
8498                 }
8499             }
8500             if (pos == 'hdrag') {
8501                 w = ow;
8502             }
8503             this.proxy.setBounds(x, y, w, h);
8504             if(this.dynamic){
8505                 this.resizeElement();
8506             }
8507             }catch(e){}
8508         }
8509         this.fireEvent("resizing", this, x, y, w, h, e);
8510     },
8511
8512     // private
8513     handleOver : function(){
8514         if(this.enabled){
8515             this.el.addClass("x-resizable-over");
8516         }
8517     },
8518
8519     // private
8520     handleOut : function(){
8521         if(!this.resizing){
8522             this.el.removeClass("x-resizable-over");
8523         }
8524     },
8525
8526     /**
8527      * Returns the element this component is bound to.
8528      * @return {Roo.Element}
8529      */
8530     getEl : function(){
8531         return this.el;
8532     },
8533
8534     /**
8535      * Returns the resizeChild element (or null).
8536      * @return {Roo.Element}
8537      */
8538     getResizeChild : function(){
8539         return this.resizeChild;
8540     },
8541     groupHandler : function()
8542     {
8543         
8544     },
8545     /**
8546      * Destroys this resizable. If the element was wrapped and
8547      * removeEl is not true then the element remains.
8548      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
8549      */
8550     destroy : function(removeEl){
8551         this.proxy.remove();
8552         if(this.overlay){
8553             this.overlay.removeAllListeners();
8554             this.overlay.remove();
8555         }
8556         var ps = Roo.Resizable.positions;
8557         for(var k in ps){
8558             if(typeof ps[k] != "function" && this[ps[k]]){
8559                 var h = this[ps[k]];
8560                 h.el.removeAllListeners();
8561                 h.el.remove();
8562             }
8563         }
8564         if(removeEl){
8565             this.el.update("");
8566             this.el.remove();
8567         }
8568     }
8569 });
8570
8571 // private
8572 // hash to map config positions to true positions
8573 Roo.Resizable.positions = {
8574     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
8575     hd: "hdrag"
8576 };
8577
8578 // private
8579 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
8580     if(!this.tpl){
8581         // only initialize the template if resizable is used
8582         var tpl = Roo.DomHelper.createTemplate(
8583             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
8584         );
8585         tpl.compile();
8586         Roo.Resizable.Handle.prototype.tpl = tpl;
8587     }
8588     this.position = pos;
8589     this.rz = rz;
8590     // show north drag fro topdra
8591     var handlepos = pos == 'hdrag' ? 'north' : pos;
8592     
8593     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
8594     if (pos == 'hdrag') {
8595         this.el.setStyle('cursor', 'pointer');
8596     }
8597     this.el.unselectable();
8598     if(transparent){
8599         this.el.setOpacity(0);
8600     }
8601     this.el.on("mousedown", this.onMouseDown, this);
8602     if(!disableTrackOver){
8603         this.el.on("mouseover", this.onMouseOver, this);
8604         this.el.on("mouseout", this.onMouseOut, this);
8605     }
8606 };
8607
8608 // private
8609 Roo.Resizable.Handle.prototype = {
8610     afterResize : function(rz){
8611         Roo.log('after?');
8612         // do nothing
8613     },
8614     // private
8615     onMouseDown : function(e){
8616         this.rz.onMouseDown(this, e);
8617     },
8618     // private
8619     onMouseOver : function(e){
8620         this.rz.handleOver(this, e);
8621     },
8622     // private
8623     onMouseOut : function(e){
8624         this.rz.handleOut(this, e);
8625     }
8626 };/*
8627  * Based on:
8628  * Ext JS Library 1.1.1
8629  * Copyright(c) 2006-2007, Ext JS, LLC.
8630  *
8631  * Originally Released Under LGPL - original licence link has changed is not relivant.
8632  *
8633  * Fork - LGPL
8634  * <script type="text/javascript">
8635  */
8636
8637 /**
8638  * @class Roo.Editor
8639  * @extends Roo.Component
8640  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
8641  * @constructor
8642  * Create a new Editor
8643  * @param {Roo.form.Field} field The Field object (or descendant)
8644  * @param {Object} config The config object
8645  */
8646 Roo.Editor = function(field, config){
8647     Roo.Editor.superclass.constructor.call(this, config);
8648     this.field = field;
8649     this.addEvents({
8650         /**
8651              * @event beforestartedit
8652              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
8653              * false from the handler of this event.
8654              * @param {Editor} this
8655              * @param {Roo.Element} boundEl The underlying element bound to this editor
8656              * @param {Mixed} value The field value being set
8657              */
8658         "beforestartedit" : true,
8659         /**
8660              * @event startedit
8661              * Fires when this editor is displayed
8662              * @param {Roo.Element} boundEl The underlying element bound to this editor
8663              * @param {Mixed} value The starting field value
8664              */
8665         "startedit" : true,
8666         /**
8667              * @event beforecomplete
8668              * Fires after a change has been made to the field, but before the change is reflected in the underlying
8669              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
8670              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
8671              * event will not fire since no edit actually occurred.
8672              * @param {Editor} this
8673              * @param {Mixed} value The current field value
8674              * @param {Mixed} startValue The original field value
8675              */
8676         "beforecomplete" : true,
8677         /**
8678              * @event complete
8679              * Fires after editing is complete and any changed value has been written to the underlying field.
8680              * @param {Editor} this
8681              * @param {Mixed} value The current field value
8682              * @param {Mixed} startValue The original field value
8683              */
8684         "complete" : true,
8685         /**
8686          * @event specialkey
8687          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8688          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8689          * @param {Roo.form.Field} this
8690          * @param {Roo.EventObject} e The event object
8691          */
8692         "specialkey" : true
8693     });
8694 };
8695
8696 Roo.extend(Roo.Editor, Roo.Component, {
8697     /**
8698      * @cfg {Boolean/String} autosize
8699      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
8700      * or "height" to adopt the height only (defaults to false)
8701      */
8702     /**
8703      * @cfg {Boolean} revertInvalid
8704      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
8705      * validation fails (defaults to true)
8706      */
8707     /**
8708      * @cfg {Boolean} ignoreNoChange
8709      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
8710      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
8711      * will never be ignored.
8712      */
8713     /**
8714      * @cfg {Boolean} hideEl
8715      * False to keep the bound element visible while the editor is displayed (defaults to true)
8716      */
8717     /**
8718      * @cfg {Mixed} value
8719      * The data value of the underlying field (defaults to "")
8720      */
8721     value : "",
8722     /**
8723      * @cfg {String} alignment
8724      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
8725      */
8726     alignment: "c-c?",
8727     /**
8728      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
8729      * for bottom-right shadow (defaults to "frame")
8730      */
8731     shadow : "frame",
8732     /**
8733      * @cfg {Boolean} constrain True to constrain the editor to the viewport
8734      */
8735     constrain : false,
8736     /**
8737      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
8738      */
8739     completeOnEnter : false,
8740     /**
8741      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
8742      */
8743     cancelOnEsc : false,
8744     /**
8745      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
8746      */
8747     updateEl : false,
8748
8749     // private
8750     onRender : function(ct, position){
8751         this.el = new Roo.Layer({
8752             shadow: this.shadow,
8753             cls: "x-editor",
8754             parentEl : ct,
8755             shim : this.shim,
8756             shadowOffset:4,
8757             id: this.id,
8758             constrain: this.constrain
8759         });
8760         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
8761         if(this.field.msgTarget != 'title'){
8762             this.field.msgTarget = 'qtip';
8763         }
8764         this.field.render(this.el);
8765         if(Roo.isGecko){
8766             this.field.el.dom.setAttribute('autocomplete', 'off');
8767         }
8768         this.field.on("specialkey", this.onSpecialKey, this);
8769         if(this.swallowKeys){
8770             this.field.el.swallowEvent(['keydown','keypress']);
8771         }
8772         this.field.show();
8773         this.field.on("blur", this.onBlur, this);
8774         if(this.field.grow){
8775             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
8776         }
8777     },
8778
8779     onSpecialKey : function(field, e)
8780     {
8781         //Roo.log('editor onSpecialKey');
8782         if(this.completeOnEnter && e.getKey() == e.ENTER){
8783             e.stopEvent();
8784             this.completeEdit();
8785             return;
8786         }
8787         // do not fire special key otherwise it might hide close the editor...
8788         if(e.getKey() == e.ENTER){    
8789             return;
8790         }
8791         if(this.cancelOnEsc && e.getKey() == e.ESC){
8792             this.cancelEdit();
8793             return;
8794         } 
8795         this.fireEvent('specialkey', field, e);
8796     
8797     },
8798
8799     /**
8800      * Starts the editing process and shows the editor.
8801      * @param {String/HTMLElement/Element} el The element to edit
8802      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
8803       * to the innerHTML of el.
8804      */
8805     startEdit : function(el, value){
8806         if(this.editing){
8807             this.completeEdit();
8808         }
8809         this.boundEl = Roo.get(el);
8810         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
8811         if(!this.rendered){
8812             this.render(this.parentEl || document.body);
8813         }
8814         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
8815             return;
8816         }
8817         this.startValue = v;
8818         this.field.setValue(v);
8819         if(this.autoSize){
8820             var sz = this.boundEl.getSize();
8821             switch(this.autoSize){
8822                 case "width":
8823                 this.setSize(sz.width,  "");
8824                 break;
8825                 case "height":
8826                 this.setSize("",  sz.height);
8827                 break;
8828                 default:
8829                 this.setSize(sz.width,  sz.height);
8830             }
8831         }
8832         this.el.alignTo(this.boundEl, this.alignment);
8833         this.editing = true;
8834         if(Roo.QuickTips){
8835             Roo.QuickTips.disable();
8836         }
8837         this.show();
8838     },
8839
8840     /**
8841      * Sets the height and width of this editor.
8842      * @param {Number} width The new width
8843      * @param {Number} height The new height
8844      */
8845     setSize : function(w, h){
8846         this.field.setSize(w, h);
8847         if(this.el){
8848             this.el.sync();
8849         }
8850     },
8851
8852     /**
8853      * Realigns the editor to the bound field based on the current alignment config value.
8854      */
8855     realign : function(){
8856         this.el.alignTo(this.boundEl, this.alignment);
8857     },
8858
8859     /**
8860      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
8861      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
8862      */
8863     completeEdit : function(remainVisible){
8864         if(!this.editing){
8865             return;
8866         }
8867         var v = this.getValue();
8868         if(this.revertInvalid !== false && !this.field.isValid()){
8869             v = this.startValue;
8870             this.cancelEdit(true);
8871         }
8872         if(String(v) === String(this.startValue) && this.ignoreNoChange){
8873             this.editing = false;
8874             this.hide();
8875             return;
8876         }
8877         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
8878             this.editing = false;
8879             if(this.updateEl && this.boundEl){
8880                 this.boundEl.update(v);
8881             }
8882             if(remainVisible !== true){
8883                 this.hide();
8884             }
8885             this.fireEvent("complete", this, v, this.startValue);
8886         }
8887     },
8888
8889     // private
8890     onShow : function(){
8891         this.el.show();
8892         if(this.hideEl !== false){
8893             this.boundEl.hide();
8894         }
8895         this.field.show();
8896         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
8897             this.fixIEFocus = true;
8898             this.deferredFocus.defer(50, this);
8899         }else{
8900             this.field.focus();
8901         }
8902         this.fireEvent("startedit", this.boundEl, this.startValue);
8903     },
8904
8905     deferredFocus : function(){
8906         if(this.editing){
8907             this.field.focus();
8908         }
8909     },
8910
8911     /**
8912      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
8913      * reverted to the original starting value.
8914      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
8915      * cancel (defaults to false)
8916      */
8917     cancelEdit : function(remainVisible){
8918         if(this.editing){
8919             this.setValue(this.startValue);
8920             if(remainVisible !== true){
8921                 this.hide();
8922             }
8923         }
8924     },
8925
8926     // private
8927     onBlur : function(){
8928         if(this.allowBlur !== true && this.editing){
8929             this.completeEdit();
8930         }
8931     },
8932
8933     // private
8934     onHide : function(){
8935         if(this.editing){
8936             this.completeEdit();
8937             return;
8938         }
8939         this.field.blur();
8940         if(this.field.collapse){
8941             this.field.collapse();
8942         }
8943         this.el.hide();
8944         if(this.hideEl !== false){
8945             this.boundEl.show();
8946         }
8947         if(Roo.QuickTips){
8948             Roo.QuickTips.enable();
8949         }
8950     },
8951
8952     /**
8953      * Sets the data value of the editor
8954      * @param {Mixed} value Any valid value supported by the underlying field
8955      */
8956     setValue : function(v){
8957         this.field.setValue(v);
8958     },
8959
8960     /**
8961      * Gets the data value of the editor
8962      * @return {Mixed} The data value
8963      */
8964     getValue : function(){
8965         return this.field.getValue();
8966     }
8967 });/*
8968  * Based on:
8969  * Ext JS Library 1.1.1
8970  * Copyright(c) 2006-2007, Ext JS, LLC.
8971  *
8972  * Originally Released Under LGPL - original licence link has changed is not relivant.
8973  *
8974  * Fork - LGPL
8975  * <script type="text/javascript">
8976  */
8977  
8978 /**
8979  * @class Roo.BasicDialog
8980  * @extends Roo.util.Observable
8981  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
8982  * <pre><code>
8983 var dlg = new Roo.BasicDialog("my-dlg", {
8984     height: 200,
8985     width: 300,
8986     minHeight: 100,
8987     minWidth: 150,
8988     modal: true,
8989     proxyDrag: true,
8990     shadow: true
8991 });
8992 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
8993 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
8994 dlg.addButton('Cancel', dlg.hide, dlg);
8995 dlg.show();
8996 </code></pre>
8997   <b>A Dialog should always be a direct child of the body element.</b>
8998  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
8999  * @cfg {String} title Default text to display in the title bar (defaults to null)
9000  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9001  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
9002  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
9003  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
9004  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
9005  * (defaults to null with no animation)
9006  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
9007  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
9008  * property for valid values (defaults to 'all')
9009  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
9010  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
9011  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
9012  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
9013  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
9014  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
9015  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
9016  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
9017  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
9018  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
9019  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
9020  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
9021  * draggable = true (defaults to false)
9022  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
9023  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9024  * shadow (defaults to false)
9025  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
9026  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
9027  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
9028  * @cfg {Array} buttons Array of buttons
9029  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
9030  * @constructor
9031  * Create a new BasicDialog.
9032  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
9033  * @param {Object} config Configuration options
9034  */
9035 Roo.BasicDialog = function(el, config){
9036     this.el = Roo.get(el);
9037     var dh = Roo.DomHelper;
9038     if(!this.el && config && config.autoCreate){
9039         if(typeof config.autoCreate == "object"){
9040             if(!config.autoCreate.id){
9041                 config.autoCreate.id = el;
9042             }
9043             this.el = dh.append(document.body,
9044                         config.autoCreate, true);
9045         }else{
9046             this.el = dh.append(document.body,
9047                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
9048         }
9049     }
9050     el = this.el;
9051     el.setDisplayed(true);
9052     el.hide = this.hideAction;
9053     this.id = el.id;
9054     el.addClass("x-dlg");
9055
9056     Roo.apply(this, config);
9057
9058     this.proxy = el.createProxy("x-dlg-proxy");
9059     this.proxy.hide = this.hideAction;
9060     this.proxy.setOpacity(.5);
9061     this.proxy.hide();
9062
9063     if(config.width){
9064         el.setWidth(config.width);
9065     }
9066     if(config.height){
9067         el.setHeight(config.height);
9068     }
9069     this.size = el.getSize();
9070     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
9071         this.xy = [config.x,config.y];
9072     }else{
9073         this.xy = el.getCenterXY(true);
9074     }
9075     /** The header element @type Roo.Element */
9076     this.header = el.child("> .x-dlg-hd");
9077     /** The body element @type Roo.Element */
9078     this.body = el.child("> .x-dlg-bd");
9079     /** The footer element @type Roo.Element */
9080     this.footer = el.child("> .x-dlg-ft");
9081
9082     if(!this.header){
9083         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
9084     }
9085     if(!this.body){
9086         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
9087     }
9088
9089     this.header.unselectable();
9090     if(this.title){
9091         this.header.update(this.title);
9092     }
9093     // this element allows the dialog to be focused for keyboard event
9094     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
9095     this.focusEl.swallowEvent("click", true);
9096
9097     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
9098
9099     // wrap the body and footer for special rendering
9100     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
9101     if(this.footer){
9102         this.bwrap.dom.appendChild(this.footer.dom);
9103     }
9104
9105     this.bg = this.el.createChild({
9106         tag: "div", cls:"x-dlg-bg",
9107         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
9108     });
9109     this.centerBg = this.bg.child("div.x-dlg-bg-center");
9110
9111
9112     if(this.autoScroll !== false && !this.autoTabs){
9113         this.body.setStyle("overflow", "auto");
9114     }
9115
9116     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
9117
9118     if(this.closable !== false){
9119         this.el.addClass("x-dlg-closable");
9120         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
9121         this.close.on("click", this.closeClick, this);
9122         this.close.addClassOnOver("x-dlg-close-over");
9123     }
9124     if(this.collapsible !== false){
9125         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
9126         this.collapseBtn.on("click", this.collapseClick, this);
9127         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
9128         this.header.on("dblclick", this.collapseClick, this);
9129     }
9130     if(this.resizable !== false){
9131         this.el.addClass("x-dlg-resizable");
9132         this.resizer = new Roo.Resizable(el, {
9133             minWidth: this.minWidth || 80,
9134             minHeight:this.minHeight || 80,
9135             handles: this.resizeHandles || "all",
9136             pinned: true
9137         });
9138         this.resizer.on("beforeresize", this.beforeResize, this);
9139         this.resizer.on("resize", this.onResize, this);
9140     }
9141     if(this.draggable !== false){
9142         el.addClass("x-dlg-draggable");
9143         if (!this.proxyDrag) {
9144             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
9145         }
9146         else {
9147             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
9148         }
9149         dd.setHandleElId(this.header.id);
9150         dd.endDrag = this.endMove.createDelegate(this);
9151         dd.startDrag = this.startMove.createDelegate(this);
9152         dd.onDrag = this.onDrag.createDelegate(this);
9153         dd.scroll = false;
9154         this.dd = dd;
9155     }
9156     if(this.modal){
9157         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
9158         this.mask.enableDisplayMode("block");
9159         this.mask.hide();
9160         this.el.addClass("x-dlg-modal");
9161     }
9162     if(this.shadow){
9163         this.shadow = new Roo.Shadow({
9164             mode : typeof this.shadow == "string" ? this.shadow : "sides",
9165             offset : this.shadowOffset
9166         });
9167     }else{
9168         this.shadowOffset = 0;
9169     }
9170     if(Roo.useShims && this.shim !== false){
9171         this.shim = this.el.createShim();
9172         this.shim.hide = this.hideAction;
9173         this.shim.hide();
9174     }else{
9175         this.shim = false;
9176     }
9177     if(this.autoTabs){
9178         this.initTabs();
9179     }
9180     if (this.buttons) { 
9181         var bts= this.buttons;
9182         this.buttons = [];
9183         Roo.each(bts, function(b) {
9184             this.addButton(b);
9185         }, this);
9186     }
9187     
9188     
9189     this.addEvents({
9190         /**
9191          * @event keydown
9192          * Fires when a key is pressed
9193          * @param {Roo.BasicDialog} this
9194          * @param {Roo.EventObject} e
9195          */
9196         "keydown" : true,
9197         /**
9198          * @event move
9199          * Fires when this dialog is moved by the user.
9200          * @param {Roo.BasicDialog} this
9201          * @param {Number} x The new page X
9202          * @param {Number} y The new page Y
9203          */
9204         "move" : true,
9205         /**
9206          * @event resize
9207          * Fires when this dialog is resized by the user.
9208          * @param {Roo.BasicDialog} this
9209          * @param {Number} width The new width
9210          * @param {Number} height The new height
9211          */
9212         "resize" : true,
9213         /**
9214          * @event beforehide
9215          * Fires before this dialog is hidden.
9216          * @param {Roo.BasicDialog} this
9217          */
9218         "beforehide" : true,
9219         /**
9220          * @event hide
9221          * Fires when this dialog is hidden.
9222          * @param {Roo.BasicDialog} this
9223          */
9224         "hide" : true,
9225         /**
9226          * @event beforeshow
9227          * Fires before this dialog is shown.
9228          * @param {Roo.BasicDialog} this
9229          */
9230         "beforeshow" : true,
9231         /**
9232          * @event show
9233          * Fires when this dialog is shown.
9234          * @param {Roo.BasicDialog} this
9235          */
9236         "show" : true
9237     });
9238     el.on("keydown", this.onKeyDown, this);
9239     el.on("mousedown", this.toFront, this);
9240     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
9241     this.el.hide();
9242     Roo.DialogManager.register(this);
9243     Roo.BasicDialog.superclass.constructor.call(this);
9244 };
9245
9246 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
9247     shadowOffset: Roo.isIE ? 6 : 5,
9248     minHeight: 80,
9249     minWidth: 200,
9250     minButtonWidth: 75,
9251     defaultButton: null,
9252     buttonAlign: "right",
9253     tabTag: 'div',
9254     firstShow: true,
9255
9256     /**
9257      * Sets the dialog title text
9258      * @param {String} text The title text to display
9259      * @return {Roo.BasicDialog} this
9260      */
9261     setTitle : function(text){
9262         this.header.update(text);
9263         return this;
9264     },
9265
9266     // private
9267     closeClick : function(){
9268         this.hide();
9269     },
9270
9271     // private
9272     collapseClick : function(){
9273         this[this.collapsed ? "expand" : "collapse"]();
9274     },
9275
9276     /**
9277      * Collapses the dialog to its minimized state (only the title bar is visible).
9278      * Equivalent to the user clicking the collapse dialog button.
9279      */
9280     collapse : function(){
9281         if(!this.collapsed){
9282             this.collapsed = true;
9283             this.el.addClass("x-dlg-collapsed");
9284             this.restoreHeight = this.el.getHeight();
9285             this.resizeTo(this.el.getWidth(), this.header.getHeight());
9286         }
9287     },
9288
9289     /**
9290      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
9291      * clicking the expand dialog button.
9292      */
9293     expand : function(){
9294         if(this.collapsed){
9295             this.collapsed = false;
9296             this.el.removeClass("x-dlg-collapsed");
9297             this.resizeTo(this.el.getWidth(), this.restoreHeight);
9298         }
9299     },
9300
9301     /**
9302      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
9303      * @return {Roo.TabPanel} The tabs component
9304      */
9305     initTabs : function(){
9306         var tabs = this.getTabs();
9307         while(tabs.getTab(0)){
9308             tabs.removeTab(0);
9309         }
9310         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
9311             var dom = el.dom;
9312             tabs.addTab(Roo.id(dom), dom.title);
9313             dom.title = "";
9314         });
9315         tabs.activate(0);
9316         return tabs;
9317     },
9318
9319     // private
9320     beforeResize : function(){
9321         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
9322     },
9323
9324     // private
9325     onResize : function(){
9326         this.refreshSize();
9327         this.syncBodyHeight();
9328         this.adjustAssets();
9329         this.focus();
9330         this.fireEvent("resize", this, this.size.width, this.size.height);
9331     },
9332
9333     // private
9334     onKeyDown : function(e){
9335         if(this.isVisible()){
9336             this.fireEvent("keydown", this, e);
9337         }
9338     },
9339
9340     /**
9341      * Resizes the dialog.
9342      * @param {Number} width
9343      * @param {Number} height
9344      * @return {Roo.BasicDialog} this
9345      */
9346     resizeTo : function(width, height){
9347         this.el.setSize(width, height);
9348         this.size = {width: width, height: height};
9349         this.syncBodyHeight();
9350         if(this.fixedcenter){
9351             this.center();
9352         }
9353         if(this.isVisible()){
9354             this.constrainXY();
9355             this.adjustAssets();
9356         }
9357         this.fireEvent("resize", this, width, height);
9358         return this;
9359     },
9360
9361
9362     /**
9363      * Resizes the dialog to fit the specified content size.
9364      * @param {Number} width
9365      * @param {Number} height
9366      * @return {Roo.BasicDialog} this
9367      */
9368     setContentSize : function(w, h){
9369         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
9370         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
9371         //if(!this.el.isBorderBox()){
9372             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
9373             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
9374         //}
9375         if(this.tabs){
9376             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
9377             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
9378         }
9379         this.resizeTo(w, h);
9380         return this;
9381     },
9382
9383     /**
9384      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
9385      * executed in response to a particular key being pressed while the dialog is active.
9386      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
9387      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9388      * @param {Function} fn The function to call
9389      * @param {Object} scope (optional) The scope of the function
9390      * @return {Roo.BasicDialog} this
9391      */
9392     addKeyListener : function(key, fn, scope){
9393         var keyCode, shift, ctrl, alt;
9394         if(typeof key == "object" && !(key instanceof Array)){
9395             keyCode = key["key"];
9396             shift = key["shift"];
9397             ctrl = key["ctrl"];
9398             alt = key["alt"];
9399         }else{
9400             keyCode = key;
9401         }
9402         var handler = function(dlg, e){
9403             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
9404                 var k = e.getKey();
9405                 if(keyCode instanceof Array){
9406                     for(var i = 0, len = keyCode.length; i < len; i++){
9407                         if(keyCode[i] == k){
9408                           fn.call(scope || window, dlg, k, e);
9409                           return;
9410                         }
9411                     }
9412                 }else{
9413                     if(k == keyCode){
9414                         fn.call(scope || window, dlg, k, e);
9415                     }
9416                 }
9417             }
9418         };
9419         this.on("keydown", handler);
9420         return this;
9421     },
9422
9423     /**
9424      * Returns the TabPanel component (creates it if it doesn't exist).
9425      * Note: If you wish to simply check for the existence of tabs without creating them,
9426      * check for a null 'tabs' property.
9427      * @return {Roo.TabPanel} The tabs component
9428      */
9429     getTabs : function(){
9430         if(!this.tabs){
9431             this.el.addClass("x-dlg-auto-tabs");
9432             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
9433             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
9434         }
9435         return this.tabs;
9436     },
9437
9438     /**
9439      * Adds a button to the footer section of the dialog.
9440      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
9441      * object or a valid Roo.DomHelper element config
9442      * @param {Function} handler The function called when the button is clicked
9443      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
9444      * @return {Roo.Button} The new button
9445      */
9446     addButton : function(config, handler, scope){
9447         var dh = Roo.DomHelper;
9448         if(!this.footer){
9449             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
9450         }
9451         if(!this.btnContainer){
9452             var tb = this.footer.createChild({
9453
9454                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
9455                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
9456             }, null, true);
9457             this.btnContainer = tb.firstChild.firstChild.firstChild;
9458         }
9459         var bconfig = {
9460             handler: handler,
9461             scope: scope,
9462             minWidth: this.minButtonWidth,
9463             hideParent:true
9464         };
9465         if(typeof config == "string"){
9466             bconfig.text = config;
9467         }else{
9468             if(config.tag){
9469                 bconfig.dhconfig = config;
9470             }else{
9471                 Roo.apply(bconfig, config);
9472             }
9473         }
9474         var fc = false;
9475         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
9476             bconfig.position = Math.max(0, bconfig.position);
9477             fc = this.btnContainer.childNodes[bconfig.position];
9478         }
9479          
9480         var btn = new Roo.Button(
9481             fc ? 
9482                 this.btnContainer.insertBefore(document.createElement("td"),fc)
9483                 : this.btnContainer.appendChild(document.createElement("td")),
9484             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
9485             bconfig
9486         );
9487         this.syncBodyHeight();
9488         if(!this.buttons){
9489             /**
9490              * Array of all the buttons that have been added to this dialog via addButton
9491              * @type Array
9492              */
9493             this.buttons = [];
9494         }
9495         this.buttons.push(btn);
9496         return btn;
9497     },
9498
9499     /**
9500      * Sets the default button to be focused when the dialog is displayed.
9501      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
9502      * @return {Roo.BasicDialog} this
9503      */
9504     setDefaultButton : function(btn){
9505         this.defaultButton = btn;
9506         return this;
9507     },
9508
9509     // private
9510     getHeaderFooterHeight : function(safe){
9511         var height = 0;
9512         if(this.header){
9513            height += this.header.getHeight();
9514         }
9515         if(this.footer){
9516            var fm = this.footer.getMargins();
9517             height += (this.footer.getHeight()+fm.top+fm.bottom);
9518         }
9519         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
9520         height += this.centerBg.getPadding("tb");
9521         return height;
9522     },
9523
9524     // private
9525     syncBodyHeight : function()
9526     {
9527         var bd = this.body, // the text
9528             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
9529             bw = this.bwrap;
9530         var height = this.size.height - this.getHeaderFooterHeight(false);
9531         bd.setHeight(height-bd.getMargins("tb"));
9532         var hh = this.header.getHeight();
9533         var h = this.size.height-hh;
9534         cb.setHeight(h);
9535         
9536         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
9537         bw.setHeight(h-cb.getPadding("tb"));
9538         
9539         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
9540         bd.setWidth(bw.getWidth(true));
9541         if(this.tabs){
9542             this.tabs.syncHeight();
9543             if(Roo.isIE){
9544                 this.tabs.el.repaint();
9545             }
9546         }
9547     },
9548
9549     /**
9550      * Restores the previous state of the dialog if Roo.state is configured.
9551      * @return {Roo.BasicDialog} this
9552      */
9553     restoreState : function(){
9554         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
9555         if(box && box.width){
9556             this.xy = [box.x, box.y];
9557             this.resizeTo(box.width, box.height);
9558         }
9559         return this;
9560     },
9561
9562     // private
9563     beforeShow : function(){
9564         this.expand();
9565         if(this.fixedcenter){
9566             this.xy = this.el.getCenterXY(true);
9567         }
9568         if(this.modal){
9569             Roo.get(document.body).addClass("x-body-masked");
9570             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9571             this.mask.show();
9572         }
9573         this.constrainXY();
9574     },
9575
9576     // private
9577     animShow : function(){
9578         var b = Roo.get(this.animateTarget).getBox();
9579         this.proxy.setSize(b.width, b.height);
9580         this.proxy.setLocation(b.x, b.y);
9581         this.proxy.show();
9582         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
9583                     true, .35, this.showEl.createDelegate(this));
9584     },
9585
9586     /**
9587      * Shows the dialog.
9588      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
9589      * @return {Roo.BasicDialog} this
9590      */
9591     show : function(animateTarget){
9592         if (this.fireEvent("beforeshow", this) === false){
9593             return;
9594         }
9595         if(this.syncHeightBeforeShow){
9596             this.syncBodyHeight();
9597         }else if(this.firstShow){
9598             this.firstShow = false;
9599             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
9600         }
9601         this.animateTarget = animateTarget || this.animateTarget;
9602         if(!this.el.isVisible()){
9603             this.beforeShow();
9604             if(this.animateTarget && Roo.get(this.animateTarget)){
9605                 this.animShow();
9606             }else{
9607                 this.showEl();
9608             }
9609         }
9610         return this;
9611     },
9612
9613     // private
9614     showEl : function(){
9615         this.proxy.hide();
9616         this.el.setXY(this.xy);
9617         this.el.show();
9618         this.adjustAssets(true);
9619         this.toFront();
9620         this.focus();
9621         // IE peekaboo bug - fix found by Dave Fenwick
9622         if(Roo.isIE){
9623             this.el.repaint();
9624         }
9625         this.fireEvent("show", this);
9626     },
9627
9628     /**
9629      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
9630      * dialog itself will receive focus.
9631      */
9632     focus : function(){
9633         if(this.defaultButton){
9634             this.defaultButton.focus();
9635         }else{
9636             this.focusEl.focus();
9637         }
9638     },
9639
9640     // private
9641     constrainXY : function(){
9642         if(this.constraintoviewport !== false){
9643             if(!this.viewSize){
9644                 if(this.container){
9645                     var s = this.container.getSize();
9646                     this.viewSize = [s.width, s.height];
9647                 }else{
9648                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
9649                 }
9650             }
9651             var s = Roo.get(this.container||document).getScroll();
9652
9653             var x = this.xy[0], y = this.xy[1];
9654             var w = this.size.width, h = this.size.height;
9655             var vw = this.viewSize[0], vh = this.viewSize[1];
9656             // only move it if it needs it
9657             var moved = false;
9658             // first validate right/bottom
9659             if(x + w > vw+s.left){
9660                 x = vw - w;
9661                 moved = true;
9662             }
9663             if(y + h > vh+s.top){
9664                 y = vh - h;
9665                 moved = true;
9666             }
9667             // then make sure top/left isn't negative
9668             if(x < s.left){
9669                 x = s.left;
9670                 moved = true;
9671             }
9672             if(y < s.top){
9673                 y = s.top;
9674                 moved = true;
9675             }
9676             if(moved){
9677                 // cache xy
9678                 this.xy = [x, y];
9679                 if(this.isVisible()){
9680                     this.el.setLocation(x, y);
9681                     this.adjustAssets();
9682                 }
9683             }
9684         }
9685     },
9686
9687     // private
9688     onDrag : function(){
9689         if(!this.proxyDrag){
9690             this.xy = this.el.getXY();
9691             this.adjustAssets();
9692         }
9693     },
9694
9695     // private
9696     adjustAssets : function(doShow){
9697         var x = this.xy[0], y = this.xy[1];
9698         var w = this.size.width, h = this.size.height;
9699         if(doShow === true){
9700             if(this.shadow){
9701                 this.shadow.show(this.el);
9702             }
9703             if(this.shim){
9704                 this.shim.show();
9705             }
9706         }
9707         if(this.shadow && this.shadow.isVisible()){
9708             this.shadow.show(this.el);
9709         }
9710         if(this.shim && this.shim.isVisible()){
9711             this.shim.setBounds(x, y, w, h);
9712         }
9713     },
9714
9715     // private
9716     adjustViewport : function(w, h){
9717         if(!w || !h){
9718             w = Roo.lib.Dom.getViewWidth();
9719             h = Roo.lib.Dom.getViewHeight();
9720         }
9721         // cache the size
9722         this.viewSize = [w, h];
9723         if(this.modal && this.mask.isVisible()){
9724             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
9725             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
9726         }
9727         if(this.isVisible()){
9728             this.constrainXY();
9729         }
9730     },
9731
9732     /**
9733      * Destroys this dialog and all its supporting elements (including any tabs, shim,
9734      * shadow, proxy, mask, etc.)  Also removes all event listeners.
9735      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
9736      */
9737     destroy : function(removeEl){
9738         if(this.isVisible()){
9739             this.animateTarget = null;
9740             this.hide();
9741         }
9742         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
9743         if(this.tabs){
9744             this.tabs.destroy(removeEl);
9745         }
9746         Roo.destroy(
9747              this.shim,
9748              this.proxy,
9749              this.resizer,
9750              this.close,
9751              this.mask
9752         );
9753         if(this.dd){
9754             this.dd.unreg();
9755         }
9756         if(this.buttons){
9757            for(var i = 0, len = this.buttons.length; i < len; i++){
9758                this.buttons[i].destroy();
9759            }
9760         }
9761         this.el.removeAllListeners();
9762         if(removeEl === true){
9763             this.el.update("");
9764             this.el.remove();
9765         }
9766         Roo.DialogManager.unregister(this);
9767     },
9768
9769     // private
9770     startMove : function(){
9771         if(this.proxyDrag){
9772             this.proxy.show();
9773         }
9774         if(this.constraintoviewport !== false){
9775             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
9776         }
9777     },
9778
9779     // private
9780     endMove : function(){
9781         if(!this.proxyDrag){
9782             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
9783         }else{
9784             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
9785             this.proxy.hide();
9786         }
9787         this.refreshSize();
9788         this.adjustAssets();
9789         this.focus();
9790         this.fireEvent("move", this, this.xy[0], this.xy[1]);
9791     },
9792
9793     /**
9794      * Brings this dialog to the front of any other visible dialogs
9795      * @return {Roo.BasicDialog} this
9796      */
9797     toFront : function(){
9798         Roo.DialogManager.bringToFront(this);
9799         return this;
9800     },
9801
9802     /**
9803      * Sends this dialog to the back (under) of any other visible dialogs
9804      * @return {Roo.BasicDialog} this
9805      */
9806     toBack : function(){
9807         Roo.DialogManager.sendToBack(this);
9808         return this;
9809     },
9810
9811     /**
9812      * Centers this dialog in the viewport
9813      * @return {Roo.BasicDialog} this
9814      */
9815     center : function(){
9816         var xy = this.el.getCenterXY(true);
9817         this.moveTo(xy[0], xy[1]);
9818         return this;
9819     },
9820
9821     /**
9822      * Moves the dialog's top-left corner to the specified point
9823      * @param {Number} x
9824      * @param {Number} y
9825      * @return {Roo.BasicDialog} this
9826      */
9827     moveTo : function(x, y){
9828         this.xy = [x,y];
9829         if(this.isVisible()){
9830             this.el.setXY(this.xy);
9831             this.adjustAssets();
9832         }
9833         return this;
9834     },
9835
9836     /**
9837      * Aligns the dialog to the specified element
9838      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9839      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
9840      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9841      * @return {Roo.BasicDialog} this
9842      */
9843     alignTo : function(element, position, offsets){
9844         this.xy = this.el.getAlignToXY(element, position, offsets);
9845         if(this.isVisible()){
9846             this.el.setXY(this.xy);
9847             this.adjustAssets();
9848         }
9849         return this;
9850     },
9851
9852     /**
9853      * Anchors an element to another element and realigns it when the window is resized.
9854      * @param {String/HTMLElement/Roo.Element} element The element to align to.
9855      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
9856      * @param {Array} offsets (optional) Offset the positioning by [x, y]
9857      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
9858      * is a number, it is used as the buffer delay (defaults to 50ms).
9859      * @return {Roo.BasicDialog} this
9860      */
9861     anchorTo : function(el, alignment, offsets, monitorScroll){
9862         var action = function(){
9863             this.alignTo(el, alignment, offsets);
9864         };
9865         Roo.EventManager.onWindowResize(action, this);
9866         var tm = typeof monitorScroll;
9867         if(tm != 'undefined'){
9868             Roo.EventManager.on(window, 'scroll', action, this,
9869                 {buffer: tm == 'number' ? monitorScroll : 50});
9870         }
9871         action.call(this);
9872         return this;
9873     },
9874
9875     /**
9876      * Returns true if the dialog is visible
9877      * @return {Boolean}
9878      */
9879     isVisible : function(){
9880         return this.el.isVisible();
9881     },
9882
9883     // private
9884     animHide : function(callback){
9885         var b = Roo.get(this.animateTarget).getBox();
9886         this.proxy.show();
9887         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
9888         this.el.hide();
9889         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
9890                     this.hideEl.createDelegate(this, [callback]));
9891     },
9892
9893     /**
9894      * Hides the dialog.
9895      * @param {Function} callback (optional) Function to call when the dialog is hidden
9896      * @return {Roo.BasicDialog} this
9897      */
9898     hide : function(callback){
9899         if (this.fireEvent("beforehide", this) === false){
9900             return;
9901         }
9902         if(this.shadow){
9903             this.shadow.hide();
9904         }
9905         if(this.shim) {
9906           this.shim.hide();
9907         }
9908         // sometimes animateTarget seems to get set.. causing problems...
9909         // this just double checks..
9910         if(this.animateTarget && Roo.get(this.animateTarget)) {
9911            this.animHide(callback);
9912         }else{
9913             this.el.hide();
9914             this.hideEl(callback);
9915         }
9916         return this;
9917     },
9918
9919     // private
9920     hideEl : function(callback){
9921         this.proxy.hide();
9922         if(this.modal){
9923             this.mask.hide();
9924             Roo.get(document.body).removeClass("x-body-masked");
9925         }
9926         this.fireEvent("hide", this);
9927         if(typeof callback == "function"){
9928             callback();
9929         }
9930     },
9931
9932     // private
9933     hideAction : function(){
9934         this.setLeft("-10000px");
9935         this.setTop("-10000px");
9936         this.setStyle("visibility", "hidden");
9937     },
9938
9939     // private
9940     refreshSize : function(){
9941         this.size = this.el.getSize();
9942         this.xy = this.el.getXY();
9943         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
9944     },
9945
9946     // private
9947     // z-index is managed by the DialogManager and may be overwritten at any time
9948     setZIndex : function(index){
9949         if(this.modal){
9950             this.mask.setStyle("z-index", index);
9951         }
9952         if(this.shim){
9953             this.shim.setStyle("z-index", ++index);
9954         }
9955         if(this.shadow){
9956             this.shadow.setZIndex(++index);
9957         }
9958         this.el.setStyle("z-index", ++index);
9959         if(this.proxy){
9960             this.proxy.setStyle("z-index", ++index);
9961         }
9962         if(this.resizer){
9963             this.resizer.proxy.setStyle("z-index", ++index);
9964         }
9965
9966         this.lastZIndex = index;
9967     },
9968
9969     /**
9970      * Returns the element for this dialog
9971      * @return {Roo.Element} The underlying dialog Element
9972      */
9973     getEl : function(){
9974         return this.el;
9975     }
9976 });
9977
9978 /**
9979  * @class Roo.DialogManager
9980  * Provides global access to BasicDialogs that have been created and
9981  * support for z-indexing (layering) multiple open dialogs.
9982  */
9983 Roo.DialogManager = function(){
9984     var list = {};
9985     var accessList = [];
9986     var front = null;
9987
9988     // private
9989     var sortDialogs = function(d1, d2){
9990         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
9991     };
9992
9993     // private
9994     var orderDialogs = function(){
9995         accessList.sort(sortDialogs);
9996         var seed = Roo.DialogManager.zseed;
9997         for(var i = 0, len = accessList.length; i < len; i++){
9998             var dlg = accessList[i];
9999             if(dlg){
10000                 dlg.setZIndex(seed + (i*10));
10001             }
10002         }
10003     };
10004
10005     return {
10006         /**
10007          * The starting z-index for BasicDialogs (defaults to 9000)
10008          * @type Number The z-index value
10009          */
10010         zseed : 9000,
10011
10012         // private
10013         register : function(dlg){
10014             list[dlg.id] = dlg;
10015             accessList.push(dlg);
10016         },
10017
10018         // private
10019         unregister : function(dlg){
10020             delete list[dlg.id];
10021             var i=0;
10022             var len=0;
10023             if(!accessList.indexOf){
10024                 for(  i = 0, len = accessList.length; i < len; i++){
10025                     if(accessList[i] == dlg){
10026                         accessList.splice(i, 1);
10027                         return;
10028                     }
10029                 }
10030             }else{
10031                  i = accessList.indexOf(dlg);
10032                 if(i != -1){
10033                     accessList.splice(i, 1);
10034                 }
10035             }
10036         },
10037
10038         /**
10039          * Gets a registered dialog by id
10040          * @param {String/Object} id The id of the dialog or a dialog
10041          * @return {Roo.BasicDialog} this
10042          */
10043         get : function(id){
10044             return typeof id == "object" ? id : list[id];
10045         },
10046
10047         /**
10048          * Brings the specified dialog to the front
10049          * @param {String/Object} dlg The id of the dialog or a dialog
10050          * @return {Roo.BasicDialog} this
10051          */
10052         bringToFront : function(dlg){
10053             dlg = this.get(dlg);
10054             if(dlg != front){
10055                 front = dlg;
10056                 dlg._lastAccess = new Date().getTime();
10057                 orderDialogs();
10058             }
10059             return dlg;
10060         },
10061
10062         /**
10063          * Sends the specified dialog to the back
10064          * @param {String/Object} dlg The id of the dialog or a dialog
10065          * @return {Roo.BasicDialog} this
10066          */
10067         sendToBack : function(dlg){
10068             dlg = this.get(dlg);
10069             dlg._lastAccess = -(new Date().getTime());
10070             orderDialogs();
10071             return dlg;
10072         },
10073
10074         /**
10075          * Hides all dialogs
10076          */
10077         hideAll : function(){
10078             for(var id in list){
10079                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
10080                     list[id].hide();
10081                 }
10082             }
10083         }
10084     };
10085 }();
10086
10087 /**
10088  * @class Roo.LayoutDialog
10089  * @extends Roo.BasicDialog
10090  * Dialog which provides adjustments for working with a layout in a Dialog.
10091  * Add your necessary layout config options to the dialog's config.<br>
10092  * Example usage (including a nested layout):
10093  * <pre><code>
10094 if(!dialog){
10095     dialog = new Roo.LayoutDialog("download-dlg", {
10096         modal: true,
10097         width:600,
10098         height:450,
10099         shadow:true,
10100         minWidth:500,
10101         minHeight:350,
10102         autoTabs:true,
10103         proxyDrag:true,
10104         // layout config merges with the dialog config
10105         center:{
10106             tabPosition: "top",
10107             alwaysShowTabs: true
10108         }
10109     });
10110     dialog.addKeyListener(27, dialog.hide, dialog);
10111     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
10112     dialog.addButton("Build It!", this.getDownload, this);
10113
10114     // we can even add nested layouts
10115     var innerLayout = new Roo.BorderLayout("dl-inner", {
10116         east: {
10117             initialSize: 200,
10118             autoScroll:true,
10119             split:true
10120         },
10121         center: {
10122             autoScroll:true
10123         }
10124     });
10125     innerLayout.beginUpdate();
10126     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
10127     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
10128     innerLayout.endUpdate(true);
10129
10130     var layout = dialog.getLayout();
10131     layout.beginUpdate();
10132     layout.add("center", new Roo.ContentPanel("standard-panel",
10133                         {title: "Download the Source", fitToFrame:true}));
10134     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
10135                {title: "Build your own roo.js"}));
10136     layout.getRegion("center").showPanel(sp);
10137     layout.endUpdate();
10138 }
10139 </code></pre>
10140     * @constructor
10141     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
10142     * @param {Object} config configuration options
10143   */
10144 Roo.LayoutDialog = function(el, cfg){
10145     
10146     var config=  cfg;
10147     if (typeof(cfg) == 'undefined') {
10148         config = Roo.apply({}, el);
10149         // not sure why we use documentElement here.. - it should always be body.
10150         // IE7 borks horribly if we use documentElement.
10151         // webkit also does not like documentElement - it creates a body element...
10152         el = Roo.get( document.body || document.documentElement ).createChild();
10153         //config.autoCreate = true;
10154     }
10155     
10156     
10157     config.autoTabs = false;
10158     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
10159     this.body.setStyle({overflow:"hidden", position:"relative"});
10160     this.layout = new Roo.BorderLayout(this.body.dom, config);
10161     this.layout.monitorWindowResize = false;
10162     this.el.addClass("x-dlg-auto-layout");
10163     // fix case when center region overwrites center function
10164     this.center = Roo.BasicDialog.prototype.center;
10165     this.on("show", this.layout.layout, this.layout, true);
10166     if (config.items) {
10167         var xitems = config.items;
10168         delete config.items;
10169         Roo.each(xitems, this.addxtype, this);
10170     }
10171     
10172     
10173 };
10174 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
10175     /**
10176      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
10177      * @deprecated
10178      */
10179     endUpdate : function(){
10180         this.layout.endUpdate();
10181     },
10182
10183     /**
10184      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
10185      *  @deprecated
10186      */
10187     beginUpdate : function(){
10188         this.layout.beginUpdate();
10189     },
10190
10191     /**
10192      * Get the BorderLayout for this dialog
10193      * @return {Roo.BorderLayout}
10194      */
10195     getLayout : function(){
10196         return this.layout;
10197     },
10198
10199     showEl : function(){
10200         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
10201         if(Roo.isIE7){
10202             this.layout.layout();
10203         }
10204     },
10205
10206     // private
10207     // Use the syncHeightBeforeShow config option to control this automatically
10208     syncBodyHeight : function(){
10209         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
10210         if(this.layout){this.layout.layout();}
10211     },
10212     
10213       /**
10214      * Add an xtype element (actually adds to the layout.)
10215      * @return {Object} xdata xtype object data.
10216      */
10217     
10218     addxtype : function(c) {
10219         return this.layout.addxtype(c);
10220     }
10221 });/*
10222  * Based on:
10223  * Ext JS Library 1.1.1
10224  * Copyright(c) 2006-2007, Ext JS, LLC.
10225  *
10226  * Originally Released Under LGPL - original licence link has changed is not relivant.
10227  *
10228  * Fork - LGPL
10229  * <script type="text/javascript">
10230  */
10231  
10232 /**
10233  * @class Roo.MessageBox
10234  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
10235  * Example usage:
10236  *<pre><code>
10237 // Basic alert:
10238 Roo.Msg.alert('Status', 'Changes saved successfully.');
10239
10240 // Prompt for user data:
10241 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
10242     if (btn == 'ok'){
10243         // process text value...
10244     }
10245 });
10246
10247 // Show a dialog using config options:
10248 Roo.Msg.show({
10249    title:'Save Changes?',
10250    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
10251    buttons: Roo.Msg.YESNOCANCEL,
10252    fn: processResult,
10253    animEl: 'elId'
10254 });
10255 </code></pre>
10256  * @singleton
10257  */
10258 Roo.MessageBox = function(){
10259     var dlg, opt, mask, waitTimer;
10260     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
10261     var buttons, activeTextEl, bwidth;
10262
10263     // private
10264     var handleButton = function(button){
10265         dlg.hide();
10266         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
10267     };
10268
10269     // private
10270     var handleHide = function(){
10271         if(opt && opt.cls){
10272             dlg.el.removeClass(opt.cls);
10273         }
10274         if(waitTimer){
10275             Roo.TaskMgr.stop(waitTimer);
10276             waitTimer = null;
10277         }
10278     };
10279
10280     // private
10281     var updateButtons = function(b){
10282         var width = 0;
10283         if(!b){
10284             buttons["ok"].hide();
10285             buttons["cancel"].hide();
10286             buttons["yes"].hide();
10287             buttons["no"].hide();
10288             dlg.footer.dom.style.display = 'none';
10289             return width;
10290         }
10291         dlg.footer.dom.style.display = '';
10292         for(var k in buttons){
10293             if(typeof buttons[k] != "function"){
10294                 if(b[k]){
10295                     buttons[k].show();
10296                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
10297                     width += buttons[k].el.getWidth()+15;
10298                 }else{
10299                     buttons[k].hide();
10300                 }
10301             }
10302         }
10303         return width;
10304     };
10305
10306     // private
10307     var handleEsc = function(d, k, e){
10308         if(opt && opt.closable !== false){
10309             dlg.hide();
10310         }
10311         if(e){
10312             e.stopEvent();
10313         }
10314     };
10315
10316     return {
10317         /**
10318          * Returns a reference to the underlying {@link Roo.BasicDialog} element
10319          * @return {Roo.BasicDialog} The BasicDialog element
10320          */
10321         getDialog : function(){
10322            if(!dlg){
10323                 dlg = new Roo.BasicDialog("x-msg-box", {
10324                     autoCreate : true,
10325                     shadow: true,
10326                     draggable: true,
10327                     resizable:false,
10328                     constraintoviewport:false,
10329                     fixedcenter:true,
10330                     collapsible : false,
10331                     shim:true,
10332                     modal: true,
10333                     width:400, height:100,
10334                     buttonAlign:"center",
10335                     closeClick : function(){
10336                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
10337                             handleButton("no");
10338                         }else{
10339                             handleButton("cancel");
10340                         }
10341                     }
10342                 });
10343                 dlg.on("hide", handleHide);
10344                 mask = dlg.mask;
10345                 dlg.addKeyListener(27, handleEsc);
10346                 buttons = {};
10347                 var bt = this.buttonText;
10348                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
10349                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
10350                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
10351                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
10352                 bodyEl = dlg.body.createChild({
10353
10354                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
10355                 });
10356                 msgEl = bodyEl.dom.firstChild;
10357                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
10358                 textboxEl.enableDisplayMode();
10359                 textboxEl.addKeyListener([10,13], function(){
10360                     if(dlg.isVisible() && opt && opt.buttons){
10361                         if(opt.buttons.ok){
10362                             handleButton("ok");
10363                         }else if(opt.buttons.yes){
10364                             handleButton("yes");
10365                         }
10366                     }
10367                 });
10368                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
10369                 textareaEl.enableDisplayMode();
10370                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
10371                 progressEl.enableDisplayMode();
10372                 var pf = progressEl.dom.firstChild;
10373                 if (pf) {
10374                     pp = Roo.get(pf.firstChild);
10375                     pp.setHeight(pf.offsetHeight);
10376                 }
10377                 
10378             }
10379             return dlg;
10380         },
10381
10382         /**
10383          * Updates the message box body text
10384          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
10385          * the XHTML-compliant non-breaking space character '&amp;#160;')
10386          * @return {Roo.MessageBox} This message box
10387          */
10388         updateText : function(text){
10389             if(!dlg.isVisible() && !opt.width){
10390                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
10391             }
10392             msgEl.innerHTML = text || '&#160;';
10393       
10394             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
10395             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
10396             var w = Math.max(
10397                     Math.min(opt.width || cw , this.maxWidth), 
10398                     Math.max(opt.minWidth || this.minWidth, bwidth)
10399             );
10400             if(opt.prompt){
10401                 activeTextEl.setWidth(w);
10402             }
10403             if(dlg.isVisible()){
10404                 dlg.fixedcenter = false;
10405             }
10406             // to big, make it scroll. = But as usual stupid IE does not support
10407             // !important..
10408             
10409             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
10410                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
10411                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
10412             } else {
10413                 bodyEl.dom.style.height = '';
10414                 bodyEl.dom.style.overflowY = '';
10415             }
10416             if (cw > w) {
10417                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
10418             } else {
10419                 bodyEl.dom.style.overflowX = '';
10420             }
10421             
10422             dlg.setContentSize(w, bodyEl.getHeight());
10423             if(dlg.isVisible()){
10424                 dlg.fixedcenter = true;
10425             }
10426             return this;
10427         },
10428
10429         /**
10430          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
10431          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
10432          * @param {Number} value Any number between 0 and 1 (e.g., .5)
10433          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
10434          * @return {Roo.MessageBox} This message box
10435          */
10436         updateProgress : function(value, text){
10437             if(text){
10438                 this.updateText(text);
10439             }
10440             if (pp) { // weird bug on my firefox - for some reason this is not defined
10441                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
10442             }
10443             return this;
10444         },        
10445
10446         /**
10447          * Returns true if the message box is currently displayed
10448          * @return {Boolean} True if the message box is visible, else false
10449          */
10450         isVisible : function(){
10451             return dlg && dlg.isVisible();  
10452         },
10453
10454         /**
10455          * Hides the message box if it is displayed
10456          */
10457         hide : function(){
10458             if(this.isVisible()){
10459                 dlg.hide();
10460             }  
10461         },
10462
10463         /**
10464          * Displays a new message box, or reinitializes an existing message box, based on the config options
10465          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
10466          * The following config object properties are supported:
10467          * <pre>
10468 Property    Type             Description
10469 ----------  ---------------  ------------------------------------------------------------------------------------
10470 animEl            String/Element   An id or Element from which the message box should animate as it opens and
10471                                    closes (defaults to undefined)
10472 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
10473                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
10474 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
10475                                    progress and wait dialogs will ignore this property and always hide the
10476                                    close button as they can only be closed programmatically.
10477 cls               String           A custom CSS class to apply to the message box element
10478 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
10479                                    displayed (defaults to 75)
10480 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
10481                                    function will be btn (the name of the button that was clicked, if applicable,
10482                                    e.g. "ok"), and text (the value of the active text field, if applicable).
10483                                    Progress and wait dialogs will ignore this option since they do not respond to
10484                                    user actions and can only be closed programmatically, so any required function
10485                                    should be called by the same code after it closes the dialog.
10486 icon              String           A CSS class that provides a background image to be used as an icon for
10487                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
10488 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
10489 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
10490 modal             Boolean          False to allow user interaction with the page while the message box is
10491                                    displayed (defaults to true)
10492 msg               String           A string that will replace the existing message box body text (defaults
10493                                    to the XHTML-compliant non-breaking space character '&#160;')
10494 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
10495 progress          Boolean          True to display a progress bar (defaults to false)
10496 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
10497 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
10498 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
10499 title             String           The title text
10500 value             String           The string value to set into the active textbox element if displayed
10501 wait              Boolean          True to display a progress bar (defaults to false)
10502 width             Number           The width of the dialog in pixels
10503 </pre>
10504          *
10505          * Example usage:
10506          * <pre><code>
10507 Roo.Msg.show({
10508    title: 'Address',
10509    msg: 'Please enter your address:',
10510    width: 300,
10511    buttons: Roo.MessageBox.OKCANCEL,
10512    multiline: true,
10513    fn: saveAddress,
10514    animEl: 'addAddressBtn'
10515 });
10516 </code></pre>
10517          * @param {Object} config Configuration options
10518          * @return {Roo.MessageBox} This message box
10519          */
10520         show : function(options)
10521         {
10522             
10523             // this causes nightmares if you show one dialog after another
10524             // especially on callbacks..
10525              
10526             if(this.isVisible()){
10527                 
10528                 this.hide();
10529                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
10530                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
10531                 Roo.log("New Dialog Message:" +  options.msg )
10532                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
10533                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
10534                 
10535             }
10536             var d = this.getDialog();
10537             opt = options;
10538             d.setTitle(opt.title || "&#160;");
10539             d.close.setDisplayed(opt.closable !== false);
10540             activeTextEl = textboxEl;
10541             opt.prompt = opt.prompt || (opt.multiline ? true : false);
10542             if(opt.prompt){
10543                 if(opt.multiline){
10544                     textboxEl.hide();
10545                     textareaEl.show();
10546                     textareaEl.setHeight(typeof opt.multiline == "number" ?
10547                         opt.multiline : this.defaultTextHeight);
10548                     activeTextEl = textareaEl;
10549                 }else{
10550                     textboxEl.show();
10551                     textareaEl.hide();
10552                 }
10553             }else{
10554                 textboxEl.hide();
10555                 textareaEl.hide();
10556             }
10557             progressEl.setDisplayed(opt.progress === true);
10558             this.updateProgress(0);
10559             activeTextEl.dom.value = opt.value || "";
10560             if(opt.prompt){
10561                 dlg.setDefaultButton(activeTextEl);
10562             }else{
10563                 var bs = opt.buttons;
10564                 var db = null;
10565                 if(bs && bs.ok){
10566                     db = buttons["ok"];
10567                 }else if(bs && bs.yes){
10568                     db = buttons["yes"];
10569                 }
10570                 dlg.setDefaultButton(db);
10571             }
10572             bwidth = updateButtons(opt.buttons);
10573             this.updateText(opt.msg);
10574             if(opt.cls){
10575                 d.el.addClass(opt.cls);
10576             }
10577             d.proxyDrag = opt.proxyDrag === true;
10578             d.modal = opt.modal !== false;
10579             d.mask = opt.modal !== false ? mask : false;
10580             if(!d.isVisible()){
10581                 // force it to the end of the z-index stack so it gets a cursor in FF
10582                 document.body.appendChild(dlg.el.dom);
10583                 d.animateTarget = null;
10584                 d.show(options.animEl);
10585             }
10586             return this;
10587         },
10588
10589         /**
10590          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
10591          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
10592          * and closing the message box when the process is complete.
10593          * @param {String} title The title bar text
10594          * @param {String} msg The message box body text
10595          * @return {Roo.MessageBox} This message box
10596          */
10597         progress : function(title, msg){
10598             this.show({
10599                 title : title,
10600                 msg : msg,
10601                 buttons: false,
10602                 progress:true,
10603                 closable:false,
10604                 minWidth: this.minProgressWidth,
10605                 modal : true
10606             });
10607             return this;
10608         },
10609
10610         /**
10611          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
10612          * If a callback function is passed it will be called after the user clicks the button, and the
10613          * id of the button that was clicked will be passed as the only parameter to the callback
10614          * (could also be the top-right close button).
10615          * @param {String} title The title bar text
10616          * @param {String} msg The message box body text
10617          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10618          * @param {Object} scope (optional) The scope of the callback function
10619          * @return {Roo.MessageBox} This message box
10620          */
10621         alert : function(title, msg, fn, scope){
10622             this.show({
10623                 title : title,
10624                 msg : msg,
10625                 buttons: this.OK,
10626                 fn: fn,
10627                 scope : scope,
10628                 modal : true
10629             });
10630             return this;
10631         },
10632
10633         /**
10634          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
10635          * interaction while waiting for a long-running process to complete that does not have defined intervals.
10636          * You are responsible for closing the message box when the process is complete.
10637          * @param {String} msg The message box body text
10638          * @param {String} title (optional) The title bar text
10639          * @return {Roo.MessageBox} This message box
10640          */
10641         wait : function(msg, title){
10642             this.show({
10643                 title : title,
10644                 msg : msg,
10645                 buttons: false,
10646                 closable:false,
10647                 progress:true,
10648                 modal:true,
10649                 width:300,
10650                 wait:true
10651             });
10652             waitTimer = Roo.TaskMgr.start({
10653                 run: function(i){
10654                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
10655                 },
10656                 interval: 1000
10657             });
10658             return this;
10659         },
10660
10661         /**
10662          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
10663          * If a callback function is passed it will be called after the user clicks either button, and the id of the
10664          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
10665          * @param {String} title The title bar text
10666          * @param {String} msg The message box body text
10667          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10668          * @param {Object} scope (optional) The scope of the callback function
10669          * @return {Roo.MessageBox} This message box
10670          */
10671         confirm : function(title, msg, fn, scope){
10672             this.show({
10673                 title : title,
10674                 msg : msg,
10675                 buttons: this.YESNO,
10676                 fn: fn,
10677                 scope : scope,
10678                 modal : true
10679             });
10680             return this;
10681         },
10682
10683         /**
10684          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
10685          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
10686          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
10687          * (could also be the top-right close button) and the text that was entered will be passed as the two
10688          * parameters to the callback.
10689          * @param {String} title The title bar text
10690          * @param {String} msg The message box body text
10691          * @param {Function} fn (optional) The callback function invoked after the message box is closed
10692          * @param {Object} scope (optional) The scope of the callback function
10693          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
10694          * property, or the height in pixels to create the textbox (defaults to false / single-line)
10695          * @return {Roo.MessageBox} This message box
10696          */
10697         prompt : function(title, msg, fn, scope, multiline){
10698             this.show({
10699                 title : title,
10700                 msg : msg,
10701                 buttons: this.OKCANCEL,
10702                 fn: fn,
10703                 minWidth:250,
10704                 scope : scope,
10705                 prompt:true,
10706                 multiline: multiline,
10707                 modal : true
10708             });
10709             return this;
10710         },
10711
10712         /**
10713          * Button config that displays a single OK button
10714          * @type Object
10715          */
10716         OK : {ok:true},
10717         /**
10718          * Button config that displays Yes and No buttons
10719          * @type Object
10720          */
10721         YESNO : {yes:true, no:true},
10722         /**
10723          * Button config that displays OK and Cancel buttons
10724          * @type Object
10725          */
10726         OKCANCEL : {ok:true, cancel:true},
10727         /**
10728          * Button config that displays Yes, No and Cancel buttons
10729          * @type Object
10730          */
10731         YESNOCANCEL : {yes:true, no:true, cancel:true},
10732
10733         /**
10734          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
10735          * @type Number
10736          */
10737         defaultTextHeight : 75,
10738         /**
10739          * The maximum width in pixels of the message box (defaults to 600)
10740          * @type Number
10741          */
10742         maxWidth : 600,
10743         /**
10744          * The minimum width in pixels of the message box (defaults to 100)
10745          * @type Number
10746          */
10747         minWidth : 100,
10748         /**
10749          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
10750          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
10751          * @type Number
10752          */
10753         minProgressWidth : 250,
10754         /**
10755          * An object containing the default button text strings that can be overriden for localized language support.
10756          * Supported properties are: ok, cancel, yes and no.
10757          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
10758          * @type Object
10759          */
10760         buttonText : {
10761             ok : "OK",
10762             cancel : "Cancel",
10763             yes : "Yes",
10764             no : "No"
10765         }
10766     };
10767 }();
10768
10769 /**
10770  * Shorthand for {@link Roo.MessageBox}
10771  */
10772 Roo.Msg = Roo.MessageBox;/*
10773  * Based on:
10774  * Ext JS Library 1.1.1
10775  * Copyright(c) 2006-2007, Ext JS, LLC.
10776  *
10777  * Originally Released Under LGPL - original licence link has changed is not relivant.
10778  *
10779  * Fork - LGPL
10780  * <script type="text/javascript">
10781  */
10782 /**
10783  * @class Roo.QuickTips
10784  * Provides attractive and customizable tooltips for any element.
10785  * @singleton
10786  */
10787 Roo.QuickTips = function(){
10788     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
10789     var ce, bd, xy, dd;
10790     var visible = false, disabled = true, inited = false;
10791     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
10792     
10793     var onOver = function(e){
10794         if(disabled){
10795             return;
10796         }
10797         var t = e.getTarget();
10798         if(!t || t.nodeType !== 1 || t == document || t == document.body){
10799             return;
10800         }
10801         if(ce && t == ce.el){
10802             clearTimeout(hideProc);
10803             return;
10804         }
10805         if(t && tagEls[t.id]){
10806             tagEls[t.id].el = t;
10807             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
10808             return;
10809         }
10810         var ttp, et = Roo.fly(t);
10811         var ns = cfg.namespace;
10812         if(tm.interceptTitles && t.title){
10813             ttp = t.title;
10814             t.qtip = ttp;
10815             t.removeAttribute("title");
10816             e.preventDefault();
10817         }else{
10818             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
10819         }
10820         if(ttp){
10821             showProc = show.defer(tm.showDelay, tm, [{
10822                 el: t, 
10823                 text: ttp.replace(/\\n/g,'<br/>'),
10824                 width: et.getAttributeNS(ns, cfg.width),
10825                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
10826                 title: et.getAttributeNS(ns, cfg.title),
10827                     cls: et.getAttributeNS(ns, cfg.cls)
10828             }]);
10829         }
10830     };
10831     
10832     var onOut = function(e){
10833         clearTimeout(showProc);
10834         var t = e.getTarget();
10835         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
10836             hideProc = setTimeout(hide, tm.hideDelay);
10837         }
10838     };
10839     
10840     var onMove = function(e){
10841         if(disabled){
10842             return;
10843         }
10844         xy = e.getXY();
10845         xy[1] += 18;
10846         if(tm.trackMouse && ce){
10847             el.setXY(xy);
10848         }
10849     };
10850     
10851     var onDown = function(e){
10852         clearTimeout(showProc);
10853         clearTimeout(hideProc);
10854         if(!e.within(el)){
10855             if(tm.hideOnClick){
10856                 hide();
10857                 tm.disable();
10858                 tm.enable.defer(100, tm);
10859             }
10860         }
10861     };
10862     
10863     var getPad = function(){
10864         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
10865     };
10866
10867     var show = function(o){
10868         if(disabled){
10869             return;
10870         }
10871         clearTimeout(dismissProc);
10872         ce = o;
10873         if(removeCls){ // in case manually hidden
10874             el.removeClass(removeCls);
10875             removeCls = null;
10876         }
10877         if(ce.cls){
10878             el.addClass(ce.cls);
10879             removeCls = ce.cls;
10880         }
10881         if(ce.title){
10882             tipTitle.update(ce.title);
10883             tipTitle.show();
10884         }else{
10885             tipTitle.update('');
10886             tipTitle.hide();
10887         }
10888         el.dom.style.width  = tm.maxWidth+'px';
10889         //tipBody.dom.style.width = '';
10890         tipBodyText.update(o.text);
10891         var p = getPad(), w = ce.width;
10892         if(!w){
10893             var td = tipBodyText.dom;
10894             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
10895             if(aw > tm.maxWidth){
10896                 w = tm.maxWidth;
10897             }else if(aw < tm.minWidth){
10898                 w = tm.minWidth;
10899             }else{
10900                 w = aw;
10901             }
10902         }
10903         //tipBody.setWidth(w);
10904         el.setWidth(parseInt(w, 10) + p);
10905         if(ce.autoHide === false){
10906             close.setDisplayed(true);
10907             if(dd){
10908                 dd.unlock();
10909             }
10910         }else{
10911             close.setDisplayed(false);
10912             if(dd){
10913                 dd.lock();
10914             }
10915         }
10916         if(xy){
10917             el.avoidY = xy[1]-18;
10918             el.setXY(xy);
10919         }
10920         if(tm.animate){
10921             el.setOpacity(.1);
10922             el.setStyle("visibility", "visible");
10923             el.fadeIn({callback: afterShow});
10924         }else{
10925             afterShow();
10926         }
10927     };
10928     
10929     var afterShow = function(){
10930         if(ce){
10931             el.show();
10932             esc.enable();
10933             if(tm.autoDismiss && ce.autoHide !== false){
10934                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
10935             }
10936         }
10937     };
10938     
10939     var hide = function(noanim){
10940         clearTimeout(dismissProc);
10941         clearTimeout(hideProc);
10942         ce = null;
10943         if(el.isVisible()){
10944             esc.disable();
10945             if(noanim !== true && tm.animate){
10946                 el.fadeOut({callback: afterHide});
10947             }else{
10948                 afterHide();
10949             } 
10950         }
10951     };
10952     
10953     var afterHide = function(){
10954         el.hide();
10955         if(removeCls){
10956             el.removeClass(removeCls);
10957             removeCls = null;
10958         }
10959     };
10960     
10961     return {
10962         /**
10963         * @cfg {Number} minWidth
10964         * The minimum width of the quick tip (defaults to 40)
10965         */
10966        minWidth : 40,
10967         /**
10968         * @cfg {Number} maxWidth
10969         * The maximum width of the quick tip (defaults to 300)
10970         */
10971        maxWidth : 300,
10972         /**
10973         * @cfg {Boolean} interceptTitles
10974         * True to automatically use the element's DOM title value if available (defaults to false)
10975         */
10976        interceptTitles : false,
10977         /**
10978         * @cfg {Boolean} trackMouse
10979         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
10980         */
10981        trackMouse : false,
10982         /**
10983         * @cfg {Boolean} hideOnClick
10984         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
10985         */
10986        hideOnClick : true,
10987         /**
10988         * @cfg {Number} showDelay
10989         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
10990         */
10991        showDelay : 500,
10992         /**
10993         * @cfg {Number} hideDelay
10994         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
10995         */
10996        hideDelay : 200,
10997         /**
10998         * @cfg {Boolean} autoHide
10999         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
11000         * Used in conjunction with hideDelay.
11001         */
11002        autoHide : true,
11003         /**
11004         * @cfg {Boolean}
11005         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
11006         * (defaults to true).  Used in conjunction with autoDismissDelay.
11007         */
11008        autoDismiss : true,
11009         /**
11010         * @cfg {Number}
11011         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
11012         */
11013        autoDismissDelay : 5000,
11014        /**
11015         * @cfg {Boolean} animate
11016         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
11017         */
11018        animate : false,
11019
11020        /**
11021         * @cfg {String} title
11022         * Title text to display (defaults to '').  This can be any valid HTML markup.
11023         */
11024         title: '',
11025        /**
11026         * @cfg {String} text
11027         * Body text to display (defaults to '').  This can be any valid HTML markup.
11028         */
11029         text : '',
11030        /**
11031         * @cfg {String} cls
11032         * A CSS class to apply to the base quick tip element (defaults to '').
11033         */
11034         cls : '',
11035        /**
11036         * @cfg {Number} width
11037         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
11038         * minWidth or maxWidth.
11039         */
11040         width : null,
11041
11042     /**
11043      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
11044      * or display QuickTips in a page.
11045      */
11046        init : function(){
11047           tm = Roo.QuickTips;
11048           cfg = tm.tagConfig;
11049           if(!inited){
11050               if(!Roo.isReady){ // allow calling of init() before onReady
11051                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
11052                   return;
11053               }
11054               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
11055               el.fxDefaults = {stopFx: true};
11056               // maximum custom styling
11057               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
11058               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
11059               tipTitle = el.child('h3');
11060               tipTitle.enableDisplayMode("block");
11061               tipBody = el.child('div.x-tip-bd');
11062               tipBodyText = el.child('div.x-tip-bd-inner');
11063               //bdLeft = el.child('div.x-tip-bd-left');
11064               //bdRight = el.child('div.x-tip-bd-right');
11065               close = el.child('div.x-tip-close');
11066               close.enableDisplayMode("block");
11067               close.on("click", hide);
11068               var d = Roo.get(document);
11069               d.on("mousedown", onDown);
11070               d.on("mouseover", onOver);
11071               d.on("mouseout", onOut);
11072               d.on("mousemove", onMove);
11073               esc = d.addKeyListener(27, hide);
11074               esc.disable();
11075               if(Roo.dd.DD){
11076                   dd = el.initDD("default", null, {
11077                       onDrag : function(){
11078                           el.sync();  
11079                       }
11080                   });
11081                   dd.setHandleElId(tipTitle.id);
11082                   dd.lock();
11083               }
11084               inited = true;
11085           }
11086           this.enable(); 
11087        },
11088
11089     /**
11090      * Configures a new quick tip instance and assigns it to a target element.  The following config options
11091      * are supported:
11092      * <pre>
11093 Property    Type                   Description
11094 ----------  ---------------------  ------------------------------------------------------------------------
11095 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
11096      * </ul>
11097      * @param {Object} config The config object
11098      */
11099        register : function(config){
11100            var cs = config instanceof Array ? config : arguments;
11101            for(var i = 0, len = cs.length; i < len; i++) {
11102                var c = cs[i];
11103                var target = c.target;
11104                if(target){
11105                    if(target instanceof Array){
11106                        for(var j = 0, jlen = target.length; j < jlen; j++){
11107                            tagEls[target[j]] = c;
11108                        }
11109                    }else{
11110                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
11111                    }
11112                }
11113            }
11114        },
11115
11116     /**
11117      * Removes this quick tip from its element and destroys it.
11118      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
11119      */
11120        unregister : function(el){
11121            delete tagEls[Roo.id(el)];
11122        },
11123
11124     /**
11125      * Enable this quick tip.
11126      */
11127        enable : function(){
11128            if(inited && disabled){
11129                locks.pop();
11130                if(locks.length < 1){
11131                    disabled = false;
11132                }
11133            }
11134        },
11135
11136     /**
11137      * Disable this quick tip.
11138      */
11139        disable : function(){
11140           disabled = true;
11141           clearTimeout(showProc);
11142           clearTimeout(hideProc);
11143           clearTimeout(dismissProc);
11144           if(ce){
11145               hide(true);
11146           }
11147           locks.push(1);
11148        },
11149
11150     /**
11151      * Returns true if the quick tip is enabled, else false.
11152      */
11153        isEnabled : function(){
11154             return !disabled;
11155        },
11156
11157         // private
11158        tagConfig : {
11159            namespace : "roo", // was ext?? this may break..
11160            alt_namespace : "ext",
11161            attribute : "qtip",
11162            width : "width",
11163            target : "target",
11164            title : "qtitle",
11165            hide : "hide",
11166            cls : "qclass"
11167        }
11168    };
11169 }();
11170
11171 // backwards compat
11172 Roo.QuickTips.tips = Roo.QuickTips.register;/*
11173  * Based on:
11174  * Ext JS Library 1.1.1
11175  * Copyright(c) 2006-2007, Ext JS, LLC.
11176  *
11177  * Originally Released Under LGPL - original licence link has changed is not relivant.
11178  *
11179  * Fork - LGPL
11180  * <script type="text/javascript">
11181  */
11182  
11183
11184 /**
11185  * @class Roo.tree.TreePanel
11186  * @extends Roo.data.Tree
11187
11188  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
11189  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
11190  * @cfg {Boolean} enableDD true to enable drag and drop
11191  * @cfg {Boolean} enableDrag true to enable just drag
11192  * @cfg {Boolean} enableDrop true to enable just drop
11193  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
11194  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
11195  * @cfg {String} ddGroup The DD group this TreePanel belongs to
11196  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
11197  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
11198  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
11199  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
11200  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
11201  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
11202  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
11203  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
11204  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
11205  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
11206  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
11207  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11208  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
11209  * 
11210  * @constructor
11211  * @param {String/HTMLElement/Element} el The container element
11212  * @param {Object} config
11213  */
11214 Roo.tree.TreePanel = function(el, config){
11215     var root = false;
11216     var loader = false;
11217     if (config.root) {
11218         root = config.root;
11219         delete config.root;
11220     }
11221     if (config.loader) {
11222         loader = config.loader;
11223         delete config.loader;
11224     }
11225     
11226     Roo.apply(this, config);
11227     Roo.tree.TreePanel.superclass.constructor.call(this);
11228     this.el = Roo.get(el);
11229     this.el.addClass('x-tree');
11230     //console.log(root);
11231     if (root) {
11232         this.setRootNode( Roo.factory(root, Roo.tree));
11233     }
11234     if (loader) {
11235         this.loader = Roo.factory(loader, Roo.tree);
11236     }
11237    /**
11238     * Read-only. The id of the container element becomes this TreePanel's id.
11239     */
11240     this.id = this.el.id;
11241     this.addEvents({
11242         /**
11243         * @event beforeload
11244         * Fires before a node is loaded, return false to cancel
11245         * @param {Node} node The node being loaded
11246         */
11247         "beforeload" : true,
11248         /**
11249         * @event load
11250         * Fires when a node is loaded
11251         * @param {Node} node The node that was loaded
11252         */
11253         "load" : true,
11254         /**
11255         * @event textchange
11256         * Fires when the text for a node is changed
11257         * @param {Node} node The node
11258         * @param {String} text The new text
11259         * @param {String} oldText The old text
11260         */
11261         "textchange" : true,
11262         /**
11263         * @event beforeexpand
11264         * Fires before a node is expanded, return false to cancel.
11265         * @param {Node} node The node
11266         * @param {Boolean} deep
11267         * @param {Boolean} anim
11268         */
11269         "beforeexpand" : true,
11270         /**
11271         * @event beforecollapse
11272         * Fires before a node is collapsed, return false to cancel.
11273         * @param {Node} node The node
11274         * @param {Boolean} deep
11275         * @param {Boolean} anim
11276         */
11277         "beforecollapse" : true,
11278         /**
11279         * @event expand
11280         * Fires when a node is expanded
11281         * @param {Node} node The node
11282         */
11283         "expand" : true,
11284         /**
11285         * @event disabledchange
11286         * Fires when the disabled status of a node changes
11287         * @param {Node} node The node
11288         * @param {Boolean} disabled
11289         */
11290         "disabledchange" : true,
11291         /**
11292         * @event collapse
11293         * Fires when a node is collapsed
11294         * @param {Node} node The node
11295         */
11296         "collapse" : true,
11297         /**
11298         * @event beforeclick
11299         * Fires before click processing on a node. Return false to cancel the default action.
11300         * @param {Node} node The node
11301         * @param {Roo.EventObject} e The event object
11302         */
11303         "beforeclick":true,
11304         /**
11305         * @event checkchange
11306         * Fires when a node with a checkbox's checked property changes
11307         * @param {Node} this This node
11308         * @param {Boolean} checked
11309         */
11310         "checkchange":true,
11311         /**
11312         * @event click
11313         * Fires when a node is clicked
11314         * @param {Node} node The node
11315         * @param {Roo.EventObject} e The event object
11316         */
11317         "click":true,
11318         /**
11319         * @event dblclick
11320         * Fires when a node is double clicked
11321         * @param {Node} node The node
11322         * @param {Roo.EventObject} e The event object
11323         */
11324         "dblclick":true,
11325         /**
11326         * @event contextmenu
11327         * Fires when a node is right clicked
11328         * @param {Node} node The node
11329         * @param {Roo.EventObject} e The event object
11330         */
11331         "contextmenu":true,
11332         /**
11333         * @event beforechildrenrendered
11334         * Fires right before the child nodes for a node are rendered
11335         * @param {Node} node The node
11336         */
11337         "beforechildrenrendered":true,
11338         /**
11339         * @event startdrag
11340         * Fires when a node starts being dragged
11341         * @param {Roo.tree.TreePanel} this
11342         * @param {Roo.tree.TreeNode} node
11343         * @param {event} e The raw browser event
11344         */ 
11345        "startdrag" : true,
11346        /**
11347         * @event enddrag
11348         * Fires when a drag operation is complete
11349         * @param {Roo.tree.TreePanel} this
11350         * @param {Roo.tree.TreeNode} node
11351         * @param {event} e The raw browser event
11352         */
11353        "enddrag" : true,
11354        /**
11355         * @event dragdrop
11356         * Fires when a dragged node is dropped on a valid DD target
11357         * @param {Roo.tree.TreePanel} this
11358         * @param {Roo.tree.TreeNode} node
11359         * @param {DD} dd The dd it was dropped on
11360         * @param {event} e The raw browser event
11361         */
11362        "dragdrop" : true,
11363        /**
11364         * @event beforenodedrop
11365         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
11366         * passed to handlers has the following properties:<br />
11367         * <ul style="padding:5px;padding-left:16px;">
11368         * <li>tree - The TreePanel</li>
11369         * <li>target - The node being targeted for the drop</li>
11370         * <li>data - The drag data from the drag source</li>
11371         * <li>point - The point of the drop - append, above or below</li>
11372         * <li>source - The drag source</li>
11373         * <li>rawEvent - Raw mouse event</li>
11374         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
11375         * to be inserted by setting them on this object.</li>
11376         * <li>cancel - Set this to true to cancel the drop.</li>
11377         * </ul>
11378         * @param {Object} dropEvent
11379         */
11380        "beforenodedrop" : true,
11381        /**
11382         * @event nodedrop
11383         * Fires after a DD object is dropped on a node in this tree. The dropEvent
11384         * passed to handlers has the following properties:<br />
11385         * <ul style="padding:5px;padding-left:16px;">
11386         * <li>tree - The TreePanel</li>
11387         * <li>target - The node being targeted for the drop</li>
11388         * <li>data - The drag data from the drag source</li>
11389         * <li>point - The point of the drop - append, above or below</li>
11390         * <li>source - The drag source</li>
11391         * <li>rawEvent - Raw mouse event</li>
11392         * <li>dropNode - Dropped node(s).</li>
11393         * </ul>
11394         * @param {Object} dropEvent
11395         */
11396        "nodedrop" : true,
11397         /**
11398         * @event nodedragover
11399         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
11400         * passed to handlers has the following properties:<br />
11401         * <ul style="padding:5px;padding-left:16px;">
11402         * <li>tree - The TreePanel</li>
11403         * <li>target - The node being targeted for the drop</li>
11404         * <li>data - The drag data from the drag source</li>
11405         * <li>point - The point of the drop - append, above or below</li>
11406         * <li>source - The drag source</li>
11407         * <li>rawEvent - Raw mouse event</li>
11408         * <li>dropNode - Drop node(s) provided by the source.</li>
11409         * <li>cancel - Set this to true to signal drop not allowed.</li>
11410         * </ul>
11411         * @param {Object} dragOverEvent
11412         */
11413        "nodedragover" : true,
11414        /**
11415         * @event appendnode
11416         * Fires when append node to the tree
11417         * @param {Roo.tree.TreePanel} this
11418         * @param {Roo.tree.TreeNode} node
11419         * @param {Number} index The index of the newly appended node
11420         */
11421        "appendnode" : true
11422         
11423     });
11424     if(this.singleExpand){
11425        this.on("beforeexpand", this.restrictExpand, this);
11426     }
11427     if (this.editor) {
11428         this.editor.tree = this;
11429         this.editor = Roo.factory(this.editor, Roo.tree);
11430     }
11431     
11432     if (this.selModel) {
11433         this.selModel = Roo.factory(this.selModel, Roo.tree);
11434     }
11435    
11436 };
11437 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
11438     rootVisible : true,
11439     animate: Roo.enableFx,
11440     lines : true,
11441     enableDD : false,
11442     hlDrop : Roo.enableFx,
11443   
11444     renderer: false,
11445     
11446     rendererTip: false,
11447     // private
11448     restrictExpand : function(node){
11449         var p = node.parentNode;
11450         if(p){
11451             if(p.expandedChild && p.expandedChild.parentNode == p){
11452                 p.expandedChild.collapse();
11453             }
11454             p.expandedChild = node;
11455         }
11456     },
11457
11458     // private override
11459     setRootNode : function(node){
11460         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
11461         if(!this.rootVisible){
11462             node.ui = new Roo.tree.RootTreeNodeUI(node);
11463         }
11464         return node;
11465     },
11466
11467     /**
11468      * Returns the container element for this TreePanel
11469      */
11470     getEl : function(){
11471         return this.el;
11472     },
11473
11474     /**
11475      * Returns the default TreeLoader for this TreePanel
11476      */
11477     getLoader : function(){
11478         return this.loader;
11479     },
11480
11481     /**
11482      * Expand all nodes
11483      */
11484     expandAll : function(){
11485         this.root.expand(true);
11486     },
11487
11488     /**
11489      * Collapse all nodes
11490      */
11491     collapseAll : function(){
11492         this.root.collapse(true);
11493     },
11494
11495     /**
11496      * Returns the selection model used by this TreePanel
11497      */
11498     getSelectionModel : function(){
11499         if(!this.selModel){
11500             this.selModel = new Roo.tree.DefaultSelectionModel();
11501         }
11502         return this.selModel;
11503     },
11504
11505     /**
11506      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
11507      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
11508      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
11509      * @return {Array}
11510      */
11511     getChecked : function(a, startNode){
11512         startNode = startNode || this.root;
11513         var r = [];
11514         var f = function(){
11515             if(this.attributes.checked){
11516                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
11517             }
11518         }
11519         startNode.cascade(f);
11520         return r;
11521     },
11522
11523     /**
11524      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11525      * @param {String} path
11526      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11527      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
11528      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
11529      */
11530     expandPath : function(path, attr, callback){
11531         attr = attr || "id";
11532         var keys = path.split(this.pathSeparator);
11533         var curNode = this.root;
11534         if(curNode.attributes[attr] != keys[1]){ // invalid root
11535             if(callback){
11536                 callback(false, null);
11537             }
11538             return;
11539         }
11540         var index = 1;
11541         var f = function(){
11542             if(++index == keys.length){
11543                 if(callback){
11544                     callback(true, curNode);
11545                 }
11546                 return;
11547             }
11548             var c = curNode.findChild(attr, keys[index]);
11549             if(!c){
11550                 if(callback){
11551                     callback(false, curNode);
11552                 }
11553                 return;
11554             }
11555             curNode = c;
11556             c.expand(false, false, f);
11557         };
11558         curNode.expand(false, false, f);
11559     },
11560
11561     /**
11562      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
11563      * @param {String} path
11564      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
11565      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
11566      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
11567      */
11568     selectPath : function(path, attr, callback){
11569         attr = attr || "id";
11570         var keys = path.split(this.pathSeparator);
11571         var v = keys.pop();
11572         if(keys.length > 0){
11573             var f = function(success, node){
11574                 if(success && node){
11575                     var n = node.findChild(attr, v);
11576                     if(n){
11577                         n.select();
11578                         if(callback){
11579                             callback(true, n);
11580                         }
11581                     }else if(callback){
11582                         callback(false, n);
11583                     }
11584                 }else{
11585                     if(callback){
11586                         callback(false, n);
11587                     }
11588                 }
11589             };
11590             this.expandPath(keys.join(this.pathSeparator), attr, f);
11591         }else{
11592             this.root.select();
11593             if(callback){
11594                 callback(true, this.root);
11595             }
11596         }
11597     },
11598
11599     getTreeEl : function(){
11600         return this.el;
11601     },
11602
11603     /**
11604      * Trigger rendering of this TreePanel
11605      */
11606     render : function(){
11607         if (this.innerCt) {
11608             return this; // stop it rendering more than once!!
11609         }
11610         
11611         this.innerCt = this.el.createChild({tag:"ul",
11612                cls:"x-tree-root-ct " +
11613                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
11614
11615         if(this.containerScroll){
11616             Roo.dd.ScrollManager.register(this.el);
11617         }
11618         if((this.enableDD || this.enableDrop) && !this.dropZone){
11619            /**
11620             * The dropZone used by this tree if drop is enabled
11621             * @type Roo.tree.TreeDropZone
11622             */
11623              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
11624                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
11625            });
11626         }
11627         if((this.enableDD || this.enableDrag) && !this.dragZone){
11628            /**
11629             * The dragZone used by this tree if drag is enabled
11630             * @type Roo.tree.TreeDragZone
11631             */
11632             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
11633                ddGroup: this.ddGroup || "TreeDD",
11634                scroll: this.ddScroll
11635            });
11636         }
11637         this.getSelectionModel().init(this);
11638         if (!this.root) {
11639             Roo.log("ROOT not set in tree");
11640             return this;
11641         }
11642         this.root.render();
11643         if(!this.rootVisible){
11644             this.root.renderChildren();
11645         }
11646         return this;
11647     }
11648 });/*
11649  * Based on:
11650  * Ext JS Library 1.1.1
11651  * Copyright(c) 2006-2007, Ext JS, LLC.
11652  *
11653  * Originally Released Under LGPL - original licence link has changed is not relivant.
11654  *
11655  * Fork - LGPL
11656  * <script type="text/javascript">
11657  */
11658  
11659
11660 /**
11661  * @class Roo.tree.DefaultSelectionModel
11662  * @extends Roo.util.Observable
11663  * The default single selection for a TreePanel.
11664  * @param {Object} cfg Configuration
11665  */
11666 Roo.tree.DefaultSelectionModel = function(cfg){
11667    this.selNode = null;
11668    
11669    
11670    
11671    this.addEvents({
11672        /**
11673         * @event selectionchange
11674         * Fires when the selected node changes
11675         * @param {DefaultSelectionModel} this
11676         * @param {TreeNode} node the new selection
11677         */
11678        "selectionchange" : true,
11679
11680        /**
11681         * @event beforeselect
11682         * Fires before the selected node changes, return false to cancel the change
11683         * @param {DefaultSelectionModel} this
11684         * @param {TreeNode} node the new selection
11685         * @param {TreeNode} node the old selection
11686         */
11687        "beforeselect" : true
11688    });
11689    
11690     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
11691 };
11692
11693 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
11694     init : function(tree){
11695         this.tree = tree;
11696         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11697         tree.on("click", this.onNodeClick, this);
11698     },
11699     
11700     onNodeClick : function(node, e){
11701         if (e.ctrlKey && this.selNode == node)  {
11702             this.unselect(node);
11703             return;
11704         }
11705         this.select(node);
11706     },
11707     
11708     /**
11709      * Select a node.
11710      * @param {TreeNode} node The node to select
11711      * @return {TreeNode} The selected node
11712      */
11713     select : function(node){
11714         var last = this.selNode;
11715         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
11716             if(last){
11717                 last.ui.onSelectedChange(false);
11718             }
11719             this.selNode = node;
11720             node.ui.onSelectedChange(true);
11721             this.fireEvent("selectionchange", this, node, last);
11722         }
11723         return node;
11724     },
11725     
11726     /**
11727      * Deselect a node.
11728      * @param {TreeNode} node The node to unselect
11729      */
11730     unselect : function(node){
11731         if(this.selNode == node){
11732             this.clearSelections();
11733         }    
11734     },
11735     
11736     /**
11737      * Clear all selections
11738      */
11739     clearSelections : function(){
11740         var n = this.selNode;
11741         if(n){
11742             n.ui.onSelectedChange(false);
11743             this.selNode = null;
11744             this.fireEvent("selectionchange", this, null);
11745         }
11746         return n;
11747     },
11748     
11749     /**
11750      * Get the selected node
11751      * @return {TreeNode} The selected node
11752      */
11753     getSelectedNode : function(){
11754         return this.selNode;    
11755     },
11756     
11757     /**
11758      * Returns true if the node is selected
11759      * @param {TreeNode} node The node to check
11760      * @return {Boolean}
11761      */
11762     isSelected : function(node){
11763         return this.selNode == node;  
11764     },
11765
11766     /**
11767      * Selects the node above the selected node in the tree, intelligently walking the nodes
11768      * @return TreeNode The new selection
11769      */
11770     selectPrevious : function(){
11771         var s = this.selNode || this.lastSelNode;
11772         if(!s){
11773             return null;
11774         }
11775         var ps = s.previousSibling;
11776         if(ps){
11777             if(!ps.isExpanded() || ps.childNodes.length < 1){
11778                 return this.select(ps);
11779             } else{
11780                 var lc = ps.lastChild;
11781                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
11782                     lc = lc.lastChild;
11783                 }
11784                 return this.select(lc);
11785             }
11786         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
11787             return this.select(s.parentNode);
11788         }
11789         return null;
11790     },
11791
11792     /**
11793      * Selects the node above the selected node in the tree, intelligently walking the nodes
11794      * @return TreeNode The new selection
11795      */
11796     selectNext : function(){
11797         var s = this.selNode || this.lastSelNode;
11798         if(!s){
11799             return null;
11800         }
11801         if(s.firstChild && s.isExpanded()){
11802              return this.select(s.firstChild);
11803          }else if(s.nextSibling){
11804              return this.select(s.nextSibling);
11805          }else if(s.parentNode){
11806             var newS = null;
11807             s.parentNode.bubble(function(){
11808                 if(this.nextSibling){
11809                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
11810                     return false;
11811                 }
11812             });
11813             return newS;
11814          }
11815         return null;
11816     },
11817
11818     onKeyDown : function(e){
11819         var s = this.selNode || this.lastSelNode;
11820         // undesirable, but required
11821         var sm = this;
11822         if(!s){
11823             return;
11824         }
11825         var k = e.getKey();
11826         switch(k){
11827              case e.DOWN:
11828                  e.stopEvent();
11829                  this.selectNext();
11830              break;
11831              case e.UP:
11832                  e.stopEvent();
11833                  this.selectPrevious();
11834              break;
11835              case e.RIGHT:
11836                  e.preventDefault();
11837                  if(s.hasChildNodes()){
11838                      if(!s.isExpanded()){
11839                          s.expand();
11840                      }else if(s.firstChild){
11841                          this.select(s.firstChild, e);
11842                      }
11843                  }
11844              break;
11845              case e.LEFT:
11846                  e.preventDefault();
11847                  if(s.hasChildNodes() && s.isExpanded()){
11848                      s.collapse();
11849                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
11850                      this.select(s.parentNode, e);
11851                  }
11852              break;
11853         };
11854     }
11855 });
11856
11857 /**
11858  * @class Roo.tree.MultiSelectionModel
11859  * @extends Roo.util.Observable
11860  * Multi selection for a TreePanel.
11861  * @param {Object} cfg Configuration
11862  */
11863 Roo.tree.MultiSelectionModel = function(){
11864    this.selNodes = [];
11865    this.selMap = {};
11866    this.addEvents({
11867        /**
11868         * @event selectionchange
11869         * Fires when the selected nodes change
11870         * @param {MultiSelectionModel} this
11871         * @param {Array} nodes Array of the selected nodes
11872         */
11873        "selectionchange" : true
11874    });
11875    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
11876    
11877 };
11878
11879 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
11880     init : function(tree){
11881         this.tree = tree;
11882         tree.getTreeEl().on("keydown", this.onKeyDown, this);
11883         tree.on("click", this.onNodeClick, this);
11884     },
11885     
11886     onNodeClick : function(node, e){
11887         this.select(node, e, e.ctrlKey);
11888     },
11889     
11890     /**
11891      * Select a node.
11892      * @param {TreeNode} node The node to select
11893      * @param {EventObject} e (optional) An event associated with the selection
11894      * @param {Boolean} keepExisting True to retain existing selections
11895      * @return {TreeNode} The selected node
11896      */
11897     select : function(node, e, keepExisting){
11898         if(keepExisting !== true){
11899             this.clearSelections(true);
11900         }
11901         if(this.isSelected(node)){
11902             this.lastSelNode = node;
11903             return node;
11904         }
11905         this.selNodes.push(node);
11906         this.selMap[node.id] = node;
11907         this.lastSelNode = node;
11908         node.ui.onSelectedChange(true);
11909         this.fireEvent("selectionchange", this, this.selNodes);
11910         return node;
11911     },
11912     
11913     /**
11914      * Deselect a node.
11915      * @param {TreeNode} node The node to unselect
11916      */
11917     unselect : function(node){
11918         if(this.selMap[node.id]){
11919             node.ui.onSelectedChange(false);
11920             var sn = this.selNodes;
11921             var index = -1;
11922             if(sn.indexOf){
11923                 index = sn.indexOf(node);
11924             }else{
11925                 for(var i = 0, len = sn.length; i < len; i++){
11926                     if(sn[i] == node){
11927                         index = i;
11928                         break;
11929                     }
11930                 }
11931             }
11932             if(index != -1){
11933                 this.selNodes.splice(index, 1);
11934             }
11935             delete this.selMap[node.id];
11936             this.fireEvent("selectionchange", this, this.selNodes);
11937         }
11938     },
11939     
11940     /**
11941      * Clear all selections
11942      */
11943     clearSelections : function(suppressEvent){
11944         var sn = this.selNodes;
11945         if(sn.length > 0){
11946             for(var i = 0, len = sn.length; i < len; i++){
11947                 sn[i].ui.onSelectedChange(false);
11948             }
11949             this.selNodes = [];
11950             this.selMap = {};
11951             if(suppressEvent !== true){
11952                 this.fireEvent("selectionchange", this, this.selNodes);
11953             }
11954         }
11955     },
11956     
11957     /**
11958      * Returns true if the node is selected
11959      * @param {TreeNode} node The node to check
11960      * @return {Boolean}
11961      */
11962     isSelected : function(node){
11963         return this.selMap[node.id] ? true : false;  
11964     },
11965     
11966     /**
11967      * Returns an array of the selected nodes
11968      * @return {Array}
11969      */
11970     getSelectedNodes : function(){
11971         return this.selNodes;    
11972     },
11973
11974     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
11975
11976     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
11977
11978     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
11979 });/*
11980  * Based on:
11981  * Ext JS Library 1.1.1
11982  * Copyright(c) 2006-2007, Ext JS, LLC.
11983  *
11984  * Originally Released Under LGPL - original licence link has changed is not relivant.
11985  *
11986  * Fork - LGPL
11987  * <script type="text/javascript">
11988  */
11989  
11990 /**
11991  * @class Roo.tree.TreeNode
11992  * @extends Roo.data.Node
11993  * @cfg {String} text The text for this node
11994  * @cfg {Boolean} expanded true to start the node expanded
11995  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
11996  * @cfg {Boolean} allowDrop false if this node cannot be drop on
11997  * @cfg {Boolean} disabled true to start the node disabled
11998  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
11999  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
12000  * @cfg {String} cls A css class to be added to the node
12001  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
12002  * @cfg {String} href URL of the link used for the node (defaults to #)
12003  * @cfg {String} hrefTarget target frame for the link
12004  * @cfg {String} qtip An Ext QuickTip for the node
12005  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
12006  * @cfg {Boolean} singleClickExpand True for single click expand on this node
12007  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
12008  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
12009  * (defaults to undefined with no checkbox rendered)
12010  * @constructor
12011  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
12012  */
12013 Roo.tree.TreeNode = function(attributes){
12014     attributes = attributes || {};
12015     if(typeof attributes == "string"){
12016         attributes = {text: attributes};
12017     }
12018     this.childrenRendered = false;
12019     this.rendered = false;
12020     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
12021     this.expanded = attributes.expanded === true;
12022     this.isTarget = attributes.isTarget !== false;
12023     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
12024     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
12025
12026     /**
12027      * Read-only. The text for this node. To change it use setText().
12028      * @type String
12029      */
12030     this.text = attributes.text;
12031     /**
12032      * True if this node is disabled.
12033      * @type Boolean
12034      */
12035     this.disabled = attributes.disabled === true;
12036
12037     this.addEvents({
12038         /**
12039         * @event textchange
12040         * Fires when the text for this node is changed
12041         * @param {Node} this This node
12042         * @param {String} text The new text
12043         * @param {String} oldText The old text
12044         */
12045         "textchange" : true,
12046         /**
12047         * @event beforeexpand
12048         * Fires before this node is expanded, return false to cancel.
12049         * @param {Node} this This node
12050         * @param {Boolean} deep
12051         * @param {Boolean} anim
12052         */
12053         "beforeexpand" : true,
12054         /**
12055         * @event beforecollapse
12056         * Fires before this node is collapsed, return false to cancel.
12057         * @param {Node} this This node
12058         * @param {Boolean} deep
12059         * @param {Boolean} anim
12060         */
12061         "beforecollapse" : true,
12062         /**
12063         * @event expand
12064         * Fires when this node is expanded
12065         * @param {Node} this This node
12066         */
12067         "expand" : true,
12068         /**
12069         * @event disabledchange
12070         * Fires when the disabled status of this node changes
12071         * @param {Node} this This node
12072         * @param {Boolean} disabled
12073         */
12074         "disabledchange" : true,
12075         /**
12076         * @event collapse
12077         * Fires when this node is collapsed
12078         * @param {Node} this This node
12079         */
12080         "collapse" : true,
12081         /**
12082         * @event beforeclick
12083         * Fires before click processing. Return false to cancel the default action.
12084         * @param {Node} this This node
12085         * @param {Roo.EventObject} e The event object
12086         */
12087         "beforeclick":true,
12088         /**
12089         * @event checkchange
12090         * Fires when a node with a checkbox's checked property changes
12091         * @param {Node} this This node
12092         * @param {Boolean} checked
12093         */
12094         "checkchange":true,
12095         /**
12096         * @event click
12097         * Fires when this node is clicked
12098         * @param {Node} this This node
12099         * @param {Roo.EventObject} e The event object
12100         */
12101         "click":true,
12102         /**
12103         * @event dblclick
12104         * Fires when this node is double clicked
12105         * @param {Node} this This node
12106         * @param {Roo.EventObject} e The event object
12107         */
12108         "dblclick":true,
12109         /**
12110         * @event contextmenu
12111         * Fires when this node is right clicked
12112         * @param {Node} this This node
12113         * @param {Roo.EventObject} e The event object
12114         */
12115         "contextmenu":true,
12116         /**
12117         * @event beforechildrenrendered
12118         * Fires right before the child nodes for this node are rendered
12119         * @param {Node} this This node
12120         */
12121         "beforechildrenrendered":true
12122     });
12123
12124     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
12125
12126     /**
12127      * Read-only. The UI for this node
12128      * @type TreeNodeUI
12129      */
12130     this.ui = new uiClass(this);
12131     
12132     // finally support items[]
12133     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
12134         return;
12135     }
12136     
12137     
12138     Roo.each(this.attributes.items, function(c) {
12139         this.appendChild(Roo.factory(c,Roo.Tree));
12140     }, this);
12141     delete this.attributes.items;
12142     
12143     
12144     
12145 };
12146 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
12147     preventHScroll: true,
12148     /**
12149      * Returns true if this node is expanded
12150      * @return {Boolean}
12151      */
12152     isExpanded : function(){
12153         return this.expanded;
12154     },
12155
12156     /**
12157      * Returns the UI object for this node
12158      * @return {TreeNodeUI}
12159      */
12160     getUI : function(){
12161         return this.ui;
12162     },
12163
12164     // private override
12165     setFirstChild : function(node){
12166         var of = this.firstChild;
12167         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
12168         if(this.childrenRendered && of && node != of){
12169             of.renderIndent(true, true);
12170         }
12171         if(this.rendered){
12172             this.renderIndent(true, true);
12173         }
12174     },
12175
12176     // private override
12177     setLastChild : function(node){
12178         var ol = this.lastChild;
12179         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
12180         if(this.childrenRendered && ol && node != ol){
12181             ol.renderIndent(true, true);
12182         }
12183         if(this.rendered){
12184             this.renderIndent(true, true);
12185         }
12186     },
12187
12188     // these methods are overridden to provide lazy rendering support
12189     // private override
12190     appendChild : function()
12191     {
12192         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
12193         if(node && this.childrenRendered){
12194             node.render();
12195         }
12196         this.ui.updateExpandIcon();
12197         return node;
12198     },
12199
12200     // private override
12201     removeChild : function(node){
12202         this.ownerTree.getSelectionModel().unselect(node);
12203         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
12204         // if it's been rendered remove dom node
12205         if(this.childrenRendered){
12206             node.ui.remove();
12207         }
12208         if(this.childNodes.length < 1){
12209             this.collapse(false, false);
12210         }else{
12211             this.ui.updateExpandIcon();
12212         }
12213         if(!this.firstChild) {
12214             this.childrenRendered = false;
12215         }
12216         return node;
12217     },
12218
12219     // private override
12220     insertBefore : function(node, refNode){
12221         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
12222         if(newNode && refNode && this.childrenRendered){
12223             node.render();
12224         }
12225         this.ui.updateExpandIcon();
12226         return newNode;
12227     },
12228
12229     /**
12230      * Sets the text for this node
12231      * @param {String} text
12232      */
12233     setText : function(text){
12234         var oldText = this.text;
12235         this.text = text;
12236         this.attributes.text = text;
12237         if(this.rendered){ // event without subscribing
12238             this.ui.onTextChange(this, text, oldText);
12239         }
12240         this.fireEvent("textchange", this, text, oldText);
12241     },
12242
12243     /**
12244      * Triggers selection of this node
12245      */
12246     select : function(){
12247         this.getOwnerTree().getSelectionModel().select(this);
12248     },
12249
12250     /**
12251      * Triggers deselection of this node
12252      */
12253     unselect : function(){
12254         this.getOwnerTree().getSelectionModel().unselect(this);
12255     },
12256
12257     /**
12258      * Returns true if this node is selected
12259      * @return {Boolean}
12260      */
12261     isSelected : function(){
12262         return this.getOwnerTree().getSelectionModel().isSelected(this);
12263     },
12264
12265     /**
12266      * Expand this node.
12267      * @param {Boolean} deep (optional) True to expand all children as well
12268      * @param {Boolean} anim (optional) false to cancel the default animation
12269      * @param {Function} callback (optional) A callback to be called when
12270      * expanding this node completes (does not wait for deep expand to complete).
12271      * Called with 1 parameter, this node.
12272      */
12273     expand : function(deep, anim, callback){
12274         if(!this.expanded){
12275             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
12276                 return;
12277             }
12278             if(!this.childrenRendered){
12279                 this.renderChildren();
12280             }
12281             this.expanded = true;
12282             
12283             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
12284                 this.ui.animExpand(function(){
12285                     this.fireEvent("expand", this);
12286                     if(typeof callback == "function"){
12287                         callback(this);
12288                     }
12289                     if(deep === true){
12290                         this.expandChildNodes(true);
12291                     }
12292                 }.createDelegate(this));
12293                 return;
12294             }else{
12295                 this.ui.expand();
12296                 this.fireEvent("expand", this);
12297                 if(typeof callback == "function"){
12298                     callback(this);
12299                 }
12300             }
12301         }else{
12302            if(typeof callback == "function"){
12303                callback(this);
12304            }
12305         }
12306         if(deep === true){
12307             this.expandChildNodes(true);
12308         }
12309     },
12310
12311     isHiddenRoot : function(){
12312         return this.isRoot && !this.getOwnerTree().rootVisible;
12313     },
12314
12315     /**
12316      * Collapse this node.
12317      * @param {Boolean} deep (optional) True to collapse all children as well
12318      * @param {Boolean} anim (optional) false to cancel the default animation
12319      */
12320     collapse : function(deep, anim){
12321         if(this.expanded && !this.isHiddenRoot()){
12322             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
12323                 return;
12324             }
12325             this.expanded = false;
12326             if((this.getOwnerTree().animate && anim !== false) || anim){
12327                 this.ui.animCollapse(function(){
12328                     this.fireEvent("collapse", this);
12329                     if(deep === true){
12330                         this.collapseChildNodes(true);
12331                     }
12332                 }.createDelegate(this));
12333                 return;
12334             }else{
12335                 this.ui.collapse();
12336                 this.fireEvent("collapse", this);
12337             }
12338         }
12339         if(deep === true){
12340             var cs = this.childNodes;
12341             for(var i = 0, len = cs.length; i < len; i++) {
12342                 cs[i].collapse(true, false);
12343             }
12344         }
12345     },
12346
12347     // private
12348     delayedExpand : function(delay){
12349         if(!this.expandProcId){
12350             this.expandProcId = this.expand.defer(delay, this);
12351         }
12352     },
12353
12354     // private
12355     cancelExpand : function(){
12356         if(this.expandProcId){
12357             clearTimeout(this.expandProcId);
12358         }
12359         this.expandProcId = false;
12360     },
12361
12362     /**
12363      * Toggles expanded/collapsed state of the node
12364      */
12365     toggle : function(){
12366         if(this.expanded){
12367             this.collapse();
12368         }else{
12369             this.expand();
12370         }
12371     },
12372
12373     /**
12374      * Ensures all parent nodes are expanded
12375      */
12376     ensureVisible : function(callback){
12377         var tree = this.getOwnerTree();
12378         tree.expandPath(this.parentNode.getPath(), false, function(){
12379             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
12380             Roo.callback(callback);
12381         }.createDelegate(this));
12382     },
12383
12384     /**
12385      * Expand all child nodes
12386      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
12387      */
12388     expandChildNodes : function(deep){
12389         var cs = this.childNodes;
12390         for(var i = 0, len = cs.length; i < len; i++) {
12391                 cs[i].expand(deep);
12392         }
12393     },
12394
12395     /**
12396      * Collapse all child nodes
12397      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
12398      */
12399     collapseChildNodes : function(deep){
12400         var cs = this.childNodes;
12401         for(var i = 0, len = cs.length; i < len; i++) {
12402                 cs[i].collapse(deep);
12403         }
12404     },
12405
12406     /**
12407      * Disables this node
12408      */
12409     disable : function(){
12410         this.disabled = true;
12411         this.unselect();
12412         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12413             this.ui.onDisableChange(this, true);
12414         }
12415         this.fireEvent("disabledchange", this, true);
12416     },
12417
12418     /**
12419      * Enables this node
12420      */
12421     enable : function(){
12422         this.disabled = false;
12423         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
12424             this.ui.onDisableChange(this, false);
12425         }
12426         this.fireEvent("disabledchange", this, false);
12427     },
12428
12429     // private
12430     renderChildren : function(suppressEvent){
12431         if(suppressEvent !== false){
12432             this.fireEvent("beforechildrenrendered", this);
12433         }
12434         var cs = this.childNodes;
12435         for(var i = 0, len = cs.length; i < len; i++){
12436             cs[i].render(true);
12437         }
12438         this.childrenRendered = true;
12439     },
12440
12441     // private
12442     sort : function(fn, scope){
12443         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
12444         if(this.childrenRendered){
12445             var cs = this.childNodes;
12446             for(var i = 0, len = cs.length; i < len; i++){
12447                 cs[i].render(true);
12448             }
12449         }
12450     },
12451
12452     // private
12453     render : function(bulkRender){
12454         this.ui.render(bulkRender);
12455         if(!this.rendered){
12456             this.rendered = true;
12457             if(this.expanded){
12458                 this.expanded = false;
12459                 this.expand(false, false);
12460             }
12461         }
12462     },
12463
12464     // private
12465     renderIndent : function(deep, refresh){
12466         if(refresh){
12467             this.ui.childIndent = null;
12468         }
12469         this.ui.renderIndent();
12470         if(deep === true && this.childrenRendered){
12471             var cs = this.childNodes;
12472             for(var i = 0, len = cs.length; i < len; i++){
12473                 cs[i].renderIndent(true, refresh);
12474             }
12475         }
12476     }
12477 });/*
12478  * Based on:
12479  * Ext JS Library 1.1.1
12480  * Copyright(c) 2006-2007, Ext JS, LLC.
12481  *
12482  * Originally Released Under LGPL - original licence link has changed is not relivant.
12483  *
12484  * Fork - LGPL
12485  * <script type="text/javascript">
12486  */
12487  
12488 /**
12489  * @class Roo.tree.AsyncTreeNode
12490  * @extends Roo.tree.TreeNode
12491  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
12492  * @constructor
12493  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
12494  */
12495  Roo.tree.AsyncTreeNode = function(config){
12496     this.loaded = false;
12497     this.loading = false;
12498     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
12499     /**
12500     * @event beforeload
12501     * Fires before this node is loaded, return false to cancel
12502     * @param {Node} this This node
12503     */
12504     this.addEvents({'beforeload':true, 'load': true});
12505     /**
12506     * @event load
12507     * Fires when this node is loaded
12508     * @param {Node} this This node
12509     */
12510     /**
12511      * The loader used by this node (defaults to using the tree's defined loader)
12512      * @type TreeLoader
12513      * @property loader
12514      */
12515 };
12516 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
12517     expand : function(deep, anim, callback){
12518         if(this.loading){ // if an async load is already running, waiting til it's done
12519             var timer;
12520             var f = function(){
12521                 if(!this.loading){ // done loading
12522                     clearInterval(timer);
12523                     this.expand(deep, anim, callback);
12524                 }
12525             }.createDelegate(this);
12526             timer = setInterval(f, 200);
12527             return;
12528         }
12529         if(!this.loaded){
12530             if(this.fireEvent("beforeload", this) === false){
12531                 return;
12532             }
12533             this.loading = true;
12534             this.ui.beforeLoad(this);
12535             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
12536             if(loader){
12537                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
12538                 return;
12539             }
12540         }
12541         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
12542     },
12543     
12544     /**
12545      * Returns true if this node is currently loading
12546      * @return {Boolean}
12547      */
12548     isLoading : function(){
12549         return this.loading;  
12550     },
12551     
12552     loadComplete : function(deep, anim, callback){
12553         this.loading = false;
12554         this.loaded = true;
12555         this.ui.afterLoad(this);
12556         this.fireEvent("load", this);
12557         this.expand(deep, anim, callback);
12558     },
12559     
12560     /**
12561      * Returns true if this node has been loaded
12562      * @return {Boolean}
12563      */
12564     isLoaded : function(){
12565         return this.loaded;
12566     },
12567     
12568     hasChildNodes : function(){
12569         if(!this.isLeaf() && !this.loaded){
12570             return true;
12571         }else{
12572             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
12573         }
12574     },
12575
12576     /**
12577      * Trigger a reload for this node
12578      * @param {Function} callback
12579      */
12580     reload : function(callback){
12581         this.collapse(false, false);
12582         while(this.firstChild){
12583             this.removeChild(this.firstChild);
12584         }
12585         this.childrenRendered = false;
12586         this.loaded = false;
12587         if(this.isHiddenRoot()){
12588             this.expanded = false;
12589         }
12590         this.expand(false, false, callback);
12591     }
12592 });/*
12593  * Based on:
12594  * Ext JS Library 1.1.1
12595  * Copyright(c) 2006-2007, Ext JS, LLC.
12596  *
12597  * Originally Released Under LGPL - original licence link has changed is not relivant.
12598  *
12599  * Fork - LGPL
12600  * <script type="text/javascript">
12601  */
12602  
12603 /**
12604  * @class Roo.tree.TreeNodeUI
12605  * @constructor
12606  * @param {Object} node The node to render
12607  * The TreeNode UI implementation is separate from the
12608  * tree implementation. Unless you are customizing the tree UI,
12609  * you should never have to use this directly.
12610  */
12611 Roo.tree.TreeNodeUI = function(node){
12612     this.node = node;
12613     this.rendered = false;
12614     this.animating = false;
12615     this.emptyIcon = Roo.BLANK_IMAGE_URL;
12616 };
12617
12618 Roo.tree.TreeNodeUI.prototype = {
12619     removeChild : function(node){
12620         if(this.rendered){
12621             this.ctNode.removeChild(node.ui.getEl());
12622         }
12623     },
12624
12625     beforeLoad : function(){
12626          this.addClass("x-tree-node-loading");
12627     },
12628
12629     afterLoad : function(){
12630          this.removeClass("x-tree-node-loading");
12631     },
12632
12633     onTextChange : function(node, text, oldText){
12634         if(this.rendered){
12635             this.textNode.innerHTML = text;
12636         }
12637     },
12638
12639     onDisableChange : function(node, state){
12640         this.disabled = state;
12641         if(state){
12642             this.addClass("x-tree-node-disabled");
12643         }else{
12644             this.removeClass("x-tree-node-disabled");
12645         }
12646     },
12647
12648     onSelectedChange : function(state){
12649         if(state){
12650             this.focus();
12651             this.addClass("x-tree-selected");
12652         }else{
12653             //this.blur();
12654             this.removeClass("x-tree-selected");
12655         }
12656     },
12657
12658     onMove : function(tree, node, oldParent, newParent, index, refNode){
12659         this.childIndent = null;
12660         if(this.rendered){
12661             var targetNode = newParent.ui.getContainer();
12662             if(!targetNode){//target not rendered
12663                 this.holder = document.createElement("div");
12664                 this.holder.appendChild(this.wrap);
12665                 return;
12666             }
12667             var insertBefore = refNode ? refNode.ui.getEl() : null;
12668             if(insertBefore){
12669                 targetNode.insertBefore(this.wrap, insertBefore);
12670             }else{
12671                 targetNode.appendChild(this.wrap);
12672             }
12673             this.node.renderIndent(true);
12674         }
12675     },
12676
12677     addClass : function(cls){
12678         if(this.elNode){
12679             Roo.fly(this.elNode).addClass(cls);
12680         }
12681     },
12682
12683     removeClass : function(cls){
12684         if(this.elNode){
12685             Roo.fly(this.elNode).removeClass(cls);
12686         }
12687     },
12688
12689     remove : function(){
12690         if(this.rendered){
12691             this.holder = document.createElement("div");
12692             this.holder.appendChild(this.wrap);
12693         }
12694     },
12695
12696     fireEvent : function(){
12697         return this.node.fireEvent.apply(this.node, arguments);
12698     },
12699
12700     initEvents : function(){
12701         this.node.on("move", this.onMove, this);
12702         var E = Roo.EventManager;
12703         var a = this.anchor;
12704
12705         var el = Roo.fly(a, '_treeui');
12706
12707         if(Roo.isOpera){ // opera render bug ignores the CSS
12708             el.setStyle("text-decoration", "none");
12709         }
12710
12711         el.on("click", this.onClick, this);
12712         el.on("dblclick", this.onDblClick, this);
12713
12714         if(this.checkbox){
12715             Roo.EventManager.on(this.checkbox,
12716                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
12717         }
12718
12719         el.on("contextmenu", this.onContextMenu, this);
12720
12721         var icon = Roo.fly(this.iconNode);
12722         icon.on("click", this.onClick, this);
12723         icon.on("dblclick", this.onDblClick, this);
12724         icon.on("contextmenu", this.onContextMenu, this);
12725         E.on(this.ecNode, "click", this.ecClick, this, true);
12726
12727         if(this.node.disabled){
12728             this.addClass("x-tree-node-disabled");
12729         }
12730         if(this.node.hidden){
12731             this.addClass("x-tree-node-disabled");
12732         }
12733         var ot = this.node.getOwnerTree();
12734         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
12735         if(dd && (!this.node.isRoot || ot.rootVisible)){
12736             Roo.dd.Registry.register(this.elNode, {
12737                 node: this.node,
12738                 handles: this.getDDHandles(),
12739                 isHandle: false
12740             });
12741         }
12742     },
12743
12744     getDDHandles : function(){
12745         return [this.iconNode, this.textNode];
12746     },
12747
12748     hide : function(){
12749         if(this.rendered){
12750             this.wrap.style.display = "none";
12751         }
12752     },
12753
12754     show : function(){
12755         if(this.rendered){
12756             this.wrap.style.display = "";
12757         }
12758     },
12759
12760     onContextMenu : function(e){
12761         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
12762             e.preventDefault();
12763             this.focus();
12764             this.fireEvent("contextmenu", this.node, e);
12765         }
12766     },
12767
12768     onClick : function(e){
12769         if(this.dropping){
12770             e.stopEvent();
12771             return;
12772         }
12773         if(this.fireEvent("beforeclick", this.node, e) !== false){
12774             if(!this.disabled && this.node.attributes.href){
12775                 this.fireEvent("click", this.node, e);
12776                 return;
12777             }
12778             e.preventDefault();
12779             if(this.disabled){
12780                 return;
12781             }
12782
12783             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
12784                 this.node.toggle();
12785             }
12786
12787             this.fireEvent("click", this.node, e);
12788         }else{
12789             e.stopEvent();
12790         }
12791     },
12792
12793     onDblClick : function(e){
12794         e.preventDefault();
12795         if(this.disabled){
12796             return;
12797         }
12798         if(this.checkbox){
12799             this.toggleCheck();
12800         }
12801         if(!this.animating && this.node.hasChildNodes()){
12802             this.node.toggle();
12803         }
12804         this.fireEvent("dblclick", this.node, e);
12805     },
12806
12807     onCheckChange : function(){
12808         var checked = this.checkbox.checked;
12809         this.node.attributes.checked = checked;
12810         this.fireEvent('checkchange', this.node, checked);
12811     },
12812
12813     ecClick : function(e){
12814         if(!this.animating && this.node.hasChildNodes()){
12815             this.node.toggle();
12816         }
12817     },
12818
12819     startDrop : function(){
12820         this.dropping = true;
12821     },
12822
12823     // delayed drop so the click event doesn't get fired on a drop
12824     endDrop : function(){
12825        setTimeout(function(){
12826            this.dropping = false;
12827        }.createDelegate(this), 50);
12828     },
12829
12830     expand : function(){
12831         this.updateExpandIcon();
12832         this.ctNode.style.display = "";
12833     },
12834
12835     focus : function(){
12836         if(!this.node.preventHScroll){
12837             try{this.anchor.focus();
12838             }catch(e){}
12839         }else if(!Roo.isIE){
12840             try{
12841                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
12842                 var l = noscroll.scrollLeft;
12843                 this.anchor.focus();
12844                 noscroll.scrollLeft = l;
12845             }catch(e){}
12846         }
12847     },
12848
12849     toggleCheck : function(value){
12850         var cb = this.checkbox;
12851         if(cb){
12852             cb.checked = (value === undefined ? !cb.checked : value);
12853         }
12854     },
12855
12856     blur : function(){
12857         try{
12858             this.anchor.blur();
12859         }catch(e){}
12860     },
12861
12862     animExpand : function(callback){
12863         var ct = Roo.get(this.ctNode);
12864         ct.stopFx();
12865         if(!this.node.hasChildNodes()){
12866             this.updateExpandIcon();
12867             this.ctNode.style.display = "";
12868             Roo.callback(callback);
12869             return;
12870         }
12871         this.animating = true;
12872         this.updateExpandIcon();
12873
12874         ct.slideIn('t', {
12875            callback : function(){
12876                this.animating = false;
12877                Roo.callback(callback);
12878             },
12879             scope: this,
12880             duration: this.node.ownerTree.duration || .25
12881         });
12882     },
12883
12884     highlight : function(){
12885         var tree = this.node.getOwnerTree();
12886         Roo.fly(this.wrap).highlight(
12887             tree.hlColor || "C3DAF9",
12888             {endColor: tree.hlBaseColor}
12889         );
12890     },
12891
12892     collapse : function(){
12893         this.updateExpandIcon();
12894         this.ctNode.style.display = "none";
12895     },
12896
12897     animCollapse : function(callback){
12898         var ct = Roo.get(this.ctNode);
12899         ct.enableDisplayMode('block');
12900         ct.stopFx();
12901
12902         this.animating = true;
12903         this.updateExpandIcon();
12904
12905         ct.slideOut('t', {
12906             callback : function(){
12907                this.animating = false;
12908                Roo.callback(callback);
12909             },
12910             scope: this,
12911             duration: this.node.ownerTree.duration || .25
12912         });
12913     },
12914
12915     getContainer : function(){
12916         return this.ctNode;
12917     },
12918
12919     getEl : function(){
12920         return this.wrap;
12921     },
12922
12923     appendDDGhost : function(ghostNode){
12924         ghostNode.appendChild(this.elNode.cloneNode(true));
12925     },
12926
12927     getDDRepairXY : function(){
12928         return Roo.lib.Dom.getXY(this.iconNode);
12929     },
12930
12931     onRender : function(){
12932         this.render();
12933     },
12934
12935     render : function(bulkRender){
12936         var n = this.node, a = n.attributes;
12937         var targetNode = n.parentNode ?
12938               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
12939
12940         if(!this.rendered){
12941             this.rendered = true;
12942
12943             this.renderElements(n, a, targetNode, bulkRender);
12944
12945             if(a.qtip){
12946                if(this.textNode.setAttributeNS){
12947                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
12948                    if(a.qtipTitle){
12949                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
12950                    }
12951                }else{
12952                    this.textNode.setAttribute("ext:qtip", a.qtip);
12953                    if(a.qtipTitle){
12954                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
12955                    }
12956                }
12957             }else if(a.qtipCfg){
12958                 a.qtipCfg.target = Roo.id(this.textNode);
12959                 Roo.QuickTips.register(a.qtipCfg);
12960             }
12961             this.initEvents();
12962             if(!this.node.expanded){
12963                 this.updateExpandIcon();
12964             }
12965         }else{
12966             if(bulkRender === true) {
12967                 targetNode.appendChild(this.wrap);
12968             }
12969         }
12970     },
12971
12972     renderElements : function(n, a, targetNode, bulkRender)
12973     {
12974         // add some indent caching, this helps performance when rendering a large tree
12975         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
12976         var t = n.getOwnerTree();
12977         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
12978         if (typeof(n.attributes.html) != 'undefined') {
12979             txt = n.attributes.html;
12980         }
12981         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
12982         var cb = typeof a.checked == 'boolean';
12983         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
12984         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
12985             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
12986             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
12987             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
12988             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
12989             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
12990              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
12991                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
12992             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
12993             "</li>"];
12994
12995         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
12996             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
12997                                 n.nextSibling.ui.getEl(), buf.join(""));
12998         }else{
12999             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
13000         }
13001
13002         this.elNode = this.wrap.childNodes[0];
13003         this.ctNode = this.wrap.childNodes[1];
13004         var cs = this.elNode.childNodes;
13005         this.indentNode = cs[0];
13006         this.ecNode = cs[1];
13007         this.iconNode = cs[2];
13008         var index = 3;
13009         if(cb){
13010             this.checkbox = cs[3];
13011             index++;
13012         }
13013         this.anchor = cs[index];
13014         this.textNode = cs[index].firstChild;
13015     },
13016
13017     getAnchor : function(){
13018         return this.anchor;
13019     },
13020
13021     getTextEl : function(){
13022         return this.textNode;
13023     },
13024
13025     getIconEl : function(){
13026         return this.iconNode;
13027     },
13028
13029     isChecked : function(){
13030         return this.checkbox ? this.checkbox.checked : false;
13031     },
13032
13033     updateExpandIcon : function(){
13034         if(this.rendered){
13035             var n = this.node, c1, c2;
13036             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
13037             var hasChild = n.hasChildNodes();
13038             if(hasChild){
13039                 if(n.expanded){
13040                     cls += "-minus";
13041                     c1 = "x-tree-node-collapsed";
13042                     c2 = "x-tree-node-expanded";
13043                 }else{
13044                     cls += "-plus";
13045                     c1 = "x-tree-node-expanded";
13046                     c2 = "x-tree-node-collapsed";
13047                 }
13048                 if(this.wasLeaf){
13049                     this.removeClass("x-tree-node-leaf");
13050                     this.wasLeaf = false;
13051                 }
13052                 if(this.c1 != c1 || this.c2 != c2){
13053                     Roo.fly(this.elNode).replaceClass(c1, c2);
13054                     this.c1 = c1; this.c2 = c2;
13055                 }
13056             }else{
13057                 // this changes non-leafs into leafs if they have no children.
13058                 // it's not very rational behaviour..
13059                 
13060                 if(!this.wasLeaf && this.node.leaf){
13061                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
13062                     delete this.c1;
13063                     delete this.c2;
13064                     this.wasLeaf = true;
13065                 }
13066             }
13067             var ecc = "x-tree-ec-icon "+cls;
13068             if(this.ecc != ecc){
13069                 this.ecNode.className = ecc;
13070                 this.ecc = ecc;
13071             }
13072         }
13073     },
13074
13075     getChildIndent : function(){
13076         if(!this.childIndent){
13077             var buf = [];
13078             var p = this.node;
13079             while(p){
13080                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
13081                     if(!p.isLast()) {
13082                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
13083                     } else {
13084                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
13085                     }
13086                 }
13087                 p = p.parentNode;
13088             }
13089             this.childIndent = buf.join("");
13090         }
13091         return this.childIndent;
13092     },
13093
13094     renderIndent : function(){
13095         if(this.rendered){
13096             var indent = "";
13097             var p = this.node.parentNode;
13098             if(p){
13099                 indent = p.ui.getChildIndent();
13100             }
13101             if(this.indentMarkup != indent){ // don't rerender if not required
13102                 this.indentNode.innerHTML = indent;
13103                 this.indentMarkup = indent;
13104             }
13105             this.updateExpandIcon();
13106         }
13107     }
13108 };
13109
13110 Roo.tree.RootTreeNodeUI = function(){
13111     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
13112 };
13113 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
13114     render : function(){
13115         if(!this.rendered){
13116             var targetNode = this.node.ownerTree.innerCt.dom;
13117             this.node.expanded = true;
13118             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
13119             this.wrap = this.ctNode = targetNode.firstChild;
13120         }
13121     },
13122     collapse : function(){
13123     },
13124     expand : function(){
13125     }
13126 });/*
13127  * Based on:
13128  * Ext JS Library 1.1.1
13129  * Copyright(c) 2006-2007, Ext JS, LLC.
13130  *
13131  * Originally Released Under LGPL - original licence link has changed is not relivant.
13132  *
13133  * Fork - LGPL
13134  * <script type="text/javascript">
13135  */
13136 /**
13137  * @class Roo.tree.TreeLoader
13138  * @extends Roo.util.Observable
13139  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
13140  * nodes from a specified URL. The response must be a javascript Array definition
13141  * who's elements are node definition objects. eg:
13142  * <pre><code>
13143 {  success : true,
13144    data :      [
13145    
13146     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
13147     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
13148     ]
13149 }
13150
13151
13152 </code></pre>
13153  * <br><br>
13154  * The old style respose with just an array is still supported, but not recommended.
13155  * <br><br>
13156  *
13157  * A server request is sent, and child nodes are loaded only when a node is expanded.
13158  * The loading node's id is passed to the server under the parameter name "node" to
13159  * enable the server to produce the correct child nodes.
13160  * <br><br>
13161  * To pass extra parameters, an event handler may be attached to the "beforeload"
13162  * event, and the parameters specified in the TreeLoader's baseParams property:
13163  * <pre><code>
13164     myTreeLoader.on("beforeload", function(treeLoader, node) {
13165         this.baseParams.category = node.attributes.category;
13166     }, this);
13167     
13168 </code></pre>
13169  *
13170  * This would pass an HTTP parameter called "category" to the server containing
13171  * the value of the Node's "category" attribute.
13172  * @constructor
13173  * Creates a new Treeloader.
13174  * @param {Object} config A config object containing config properties.
13175  */
13176 Roo.tree.TreeLoader = function(config){
13177     this.baseParams = {};
13178     this.requestMethod = "POST";
13179     Roo.apply(this, config);
13180
13181     this.addEvents({
13182     
13183         /**
13184          * @event beforeload
13185          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
13186          * @param {Object} This TreeLoader object.
13187          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13188          * @param {Object} callback The callback function specified in the {@link #load} call.
13189          */
13190         beforeload : true,
13191         /**
13192          * @event load
13193          * Fires when the node has been successfuly loaded.
13194          * @param {Object} This TreeLoader object.
13195          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13196          * @param {Object} response The response object containing the data from the server.
13197          */
13198         load : true,
13199         /**
13200          * @event loadexception
13201          * Fires if the network request failed.
13202          * @param {Object} This TreeLoader object.
13203          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
13204          * @param {Object} response The response object containing the data from the server.
13205          */
13206         loadexception : true,
13207         /**
13208          * @event create
13209          * Fires before a node is created, enabling you to return custom Node types 
13210          * @param {Object} This TreeLoader object.
13211          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
13212          */
13213         create : true
13214     });
13215
13216     Roo.tree.TreeLoader.superclass.constructor.call(this);
13217 };
13218
13219 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
13220     /**
13221     * @cfg {String} dataUrl The URL from which to request a Json string which
13222     * specifies an array of node definition object representing the child nodes
13223     * to be loaded.
13224     */
13225     /**
13226     * @cfg {String} requestMethod either GET or POST
13227     * defaults to POST (due to BC)
13228     * to be loaded.
13229     */
13230     /**
13231     * @cfg {Object} baseParams (optional) An object containing properties which
13232     * specify HTTP parameters to be passed to each request for child nodes.
13233     */
13234     /**
13235     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
13236     * created by this loader. If the attributes sent by the server have an attribute in this object,
13237     * they take priority.
13238     */
13239     /**
13240     * @cfg {Object} uiProviders (optional) An object containing properties which
13241     * 
13242     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
13243     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
13244     * <i>uiProvider</i> attribute of a returned child node is a string rather
13245     * than a reference to a TreeNodeUI implementation, this that string value
13246     * is used as a property name in the uiProviders object. You can define the provider named
13247     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
13248     */
13249     uiProviders : {},
13250
13251     /**
13252     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
13253     * child nodes before loading.
13254     */
13255     clearOnLoad : true,
13256
13257     /**
13258     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
13259     * property on loading, rather than expecting an array. (eg. more compatible to a standard
13260     * Grid query { data : [ .....] }
13261     */
13262     
13263     root : false,
13264      /**
13265     * @cfg {String} queryParam (optional) 
13266     * Name of the query as it will be passed on the querystring (defaults to 'node')
13267     * eg. the request will be ?node=[id]
13268     */
13269     
13270     
13271     queryParam: false,
13272     
13273     /**
13274      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
13275      * This is called automatically when a node is expanded, but may be used to reload
13276      * a node (or append new children if the {@link #clearOnLoad} option is false.)
13277      * @param {Roo.tree.TreeNode} node
13278      * @param {Function} callback
13279      */
13280     load : function(node, callback){
13281         if(this.clearOnLoad){
13282             while(node.firstChild){
13283                 node.removeChild(node.firstChild);
13284             }
13285         }
13286         if(node.attributes.children){ // preloaded json children
13287             var cs = node.attributes.children;
13288             for(var i = 0, len = cs.length; i < len; i++){
13289                 node.appendChild(this.createNode(cs[i]));
13290             }
13291             if(typeof callback == "function"){
13292                 callback();
13293             }
13294         }else if(this.dataUrl){
13295             this.requestData(node, callback);
13296         }
13297     },
13298
13299     getParams: function(node){
13300         var buf = [], bp = this.baseParams;
13301         for(var key in bp){
13302             if(typeof bp[key] != "function"){
13303                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
13304             }
13305         }
13306         var n = this.queryParam === false ? 'node' : this.queryParam;
13307         buf.push(n + "=", encodeURIComponent(node.id));
13308         return buf.join("");
13309     },
13310
13311     requestData : function(node, callback){
13312         if(this.fireEvent("beforeload", this, node, callback) !== false){
13313             this.transId = Roo.Ajax.request({
13314                 method:this.requestMethod,
13315                 url: this.dataUrl||this.url,
13316                 success: this.handleResponse,
13317                 failure: this.handleFailure,
13318                 scope: this,
13319                 argument: {callback: callback, node: node},
13320                 params: this.getParams(node)
13321             });
13322         }else{
13323             // if the load is cancelled, make sure we notify
13324             // the node that we are done
13325             if(typeof callback == "function"){
13326                 callback();
13327             }
13328         }
13329     },
13330
13331     isLoading : function(){
13332         return this.transId ? true : false;
13333     },
13334
13335     abort : function(){
13336         if(this.isLoading()){
13337             Roo.Ajax.abort(this.transId);
13338         }
13339     },
13340
13341     // private
13342     createNode : function(attr)
13343     {
13344         // apply baseAttrs, nice idea Corey!
13345         if(this.baseAttrs){
13346             Roo.applyIf(attr, this.baseAttrs);
13347         }
13348         if(this.applyLoader !== false){
13349             attr.loader = this;
13350         }
13351         // uiProvider = depreciated..
13352         
13353         if(typeof(attr.uiProvider) == 'string'){
13354            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
13355                 /**  eval:var:attr */ eval(attr.uiProvider);
13356         }
13357         if(typeof(this.uiProviders['default']) != 'undefined') {
13358             attr.uiProvider = this.uiProviders['default'];
13359         }
13360         
13361         this.fireEvent('create', this, attr);
13362         
13363         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
13364         return(attr.leaf ?
13365                         new Roo.tree.TreeNode(attr) :
13366                         new Roo.tree.AsyncTreeNode(attr));
13367     },
13368
13369     processResponse : function(response, node, callback)
13370     {
13371         var json = response.responseText;
13372         try {
13373             
13374             var o = Roo.decode(json);
13375             
13376             if (this.root === false && typeof(o.success) != undefined) {
13377                 this.root = 'data'; // the default behaviour for list like data..
13378                 }
13379                 
13380             if (this.root !== false &&  !o.success) {
13381                 // it's a failure condition.
13382                 var a = response.argument;
13383                 this.fireEvent("loadexception", this, a.node, response);
13384                 Roo.log("Load failed - should have a handler really");
13385                 return;
13386             }
13387             
13388             
13389             
13390             if (this.root !== false) {
13391                  o = o[this.root];
13392             }
13393             
13394             for(var i = 0, len = o.length; i < len; i++){
13395                 var n = this.createNode(o[i]);
13396                 if(n){
13397                     node.appendChild(n);
13398                 }
13399             }
13400             if(typeof callback == "function"){
13401                 callback(this, node);
13402             }
13403         }catch(e){
13404             this.handleFailure(response);
13405         }
13406     },
13407
13408     handleResponse : function(response){
13409         this.transId = false;
13410         var a = response.argument;
13411         this.processResponse(response, a.node, a.callback);
13412         this.fireEvent("load", this, a.node, response);
13413     },
13414
13415     handleFailure : function(response)
13416     {
13417         // should handle failure better..
13418         this.transId = false;
13419         var a = response.argument;
13420         this.fireEvent("loadexception", this, a.node, response);
13421         if(typeof a.callback == "function"){
13422             a.callback(this, a.node);
13423         }
13424     }
13425 });/*
13426  * Based on:
13427  * Ext JS Library 1.1.1
13428  * Copyright(c) 2006-2007, Ext JS, LLC.
13429  *
13430  * Originally Released Under LGPL - original licence link has changed is not relivant.
13431  *
13432  * Fork - LGPL
13433  * <script type="text/javascript">
13434  */
13435
13436 /**
13437 * @class Roo.tree.TreeFilter
13438 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
13439 * @param {TreePanel} tree
13440 * @param {Object} config (optional)
13441  */
13442 Roo.tree.TreeFilter = function(tree, config){
13443     this.tree = tree;
13444     this.filtered = {};
13445     Roo.apply(this, config);
13446 };
13447
13448 Roo.tree.TreeFilter.prototype = {
13449     clearBlank:false,
13450     reverse:false,
13451     autoClear:false,
13452     remove:false,
13453
13454      /**
13455      * Filter the data by a specific attribute.
13456      * @param {String/RegExp} value Either string that the attribute value
13457      * should start with or a RegExp to test against the attribute
13458      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
13459      * @param {TreeNode} startNode (optional) The node to start the filter at.
13460      */
13461     filter : function(value, attr, startNode){
13462         attr = attr || "text";
13463         var f;
13464         if(typeof value == "string"){
13465             var vlen = value.length;
13466             // auto clear empty filter
13467             if(vlen == 0 && this.clearBlank){
13468                 this.clear();
13469                 return;
13470             }
13471             value = value.toLowerCase();
13472             f = function(n){
13473                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
13474             };
13475         }else if(value.exec){ // regex?
13476             f = function(n){
13477                 return value.test(n.attributes[attr]);
13478             };
13479         }else{
13480             throw 'Illegal filter type, must be string or regex';
13481         }
13482         this.filterBy(f, null, startNode);
13483         },
13484
13485     /**
13486      * Filter by a function. The passed function will be called with each
13487      * node in the tree (or from the startNode). If the function returns true, the node is kept
13488      * otherwise it is filtered. If a node is filtered, its children are also filtered.
13489      * @param {Function} fn The filter function
13490      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
13491      */
13492     filterBy : function(fn, scope, startNode){
13493         startNode = startNode || this.tree.root;
13494         if(this.autoClear){
13495             this.clear();
13496         }
13497         var af = this.filtered, rv = this.reverse;
13498         var f = function(n){
13499             if(n == startNode){
13500                 return true;
13501             }
13502             if(af[n.id]){
13503                 return false;
13504             }
13505             var m = fn.call(scope || n, n);
13506             if(!m || rv){
13507                 af[n.id] = n;
13508                 n.ui.hide();
13509                 return false;
13510             }
13511             return true;
13512         };
13513         startNode.cascade(f);
13514         if(this.remove){
13515            for(var id in af){
13516                if(typeof id != "function"){
13517                    var n = af[id];
13518                    if(n && n.parentNode){
13519                        n.parentNode.removeChild(n);
13520                    }
13521                }
13522            }
13523         }
13524     },
13525
13526     /**
13527      * Clears the current filter. Note: with the "remove" option
13528      * set a filter cannot be cleared.
13529      */
13530     clear : function(){
13531         var t = this.tree;
13532         var af = this.filtered;
13533         for(var id in af){
13534             if(typeof id != "function"){
13535                 var n = af[id];
13536                 if(n){
13537                     n.ui.show();
13538                 }
13539             }
13540         }
13541         this.filtered = {};
13542     }
13543 };
13544 /*
13545  * Based on:
13546  * Ext JS Library 1.1.1
13547  * Copyright(c) 2006-2007, Ext JS, LLC.
13548  *
13549  * Originally Released Under LGPL - original licence link has changed is not relivant.
13550  *
13551  * Fork - LGPL
13552  * <script type="text/javascript">
13553  */
13554  
13555
13556 /**
13557  * @class Roo.tree.TreeSorter
13558  * Provides sorting of nodes in a TreePanel
13559  * 
13560  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
13561  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
13562  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
13563  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
13564  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
13565  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
13566  * @constructor
13567  * @param {TreePanel} tree
13568  * @param {Object} config
13569  */
13570 Roo.tree.TreeSorter = function(tree, config){
13571     Roo.apply(this, config);
13572     tree.on("beforechildrenrendered", this.doSort, this);
13573     tree.on("append", this.updateSort, this);
13574     tree.on("insert", this.updateSort, this);
13575     
13576     var dsc = this.dir && this.dir.toLowerCase() == "desc";
13577     var p = this.property || "text";
13578     var sortType = this.sortType;
13579     var fs = this.folderSort;
13580     var cs = this.caseSensitive === true;
13581     var leafAttr = this.leafAttr || 'leaf';
13582
13583     this.sortFn = function(n1, n2){
13584         if(fs){
13585             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
13586                 return 1;
13587             }
13588             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
13589                 return -1;
13590             }
13591         }
13592         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
13593         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
13594         if(v1 < v2){
13595                         return dsc ? +1 : -1;
13596                 }else if(v1 > v2){
13597                         return dsc ? -1 : +1;
13598         }else{
13599                 return 0;
13600         }
13601     };
13602 };
13603
13604 Roo.tree.TreeSorter.prototype = {
13605     doSort : function(node){
13606         node.sort(this.sortFn);
13607     },
13608     
13609     compareNodes : function(n1, n2){
13610         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
13611     },
13612     
13613     updateSort : function(tree, node){
13614         if(node.childrenRendered){
13615             this.doSort.defer(1, this, [node]);
13616         }
13617     }
13618 };/*
13619  * Based on:
13620  * Ext JS Library 1.1.1
13621  * Copyright(c) 2006-2007, Ext JS, LLC.
13622  *
13623  * Originally Released Under LGPL - original licence link has changed is not relivant.
13624  *
13625  * Fork - LGPL
13626  * <script type="text/javascript">
13627  */
13628
13629 if(Roo.dd.DropZone){
13630     
13631 Roo.tree.TreeDropZone = function(tree, config){
13632     this.allowParentInsert = false;
13633     this.allowContainerDrop = false;
13634     this.appendOnly = false;
13635     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
13636     this.tree = tree;
13637     this.lastInsertClass = "x-tree-no-status";
13638     this.dragOverData = {};
13639 };
13640
13641 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
13642     ddGroup : "TreeDD",
13643     scroll:  true,
13644     
13645     expandDelay : 1000,
13646     
13647     expandNode : function(node){
13648         if(node.hasChildNodes() && !node.isExpanded()){
13649             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
13650         }
13651     },
13652     
13653     queueExpand : function(node){
13654         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
13655     },
13656     
13657     cancelExpand : function(){
13658         if(this.expandProcId){
13659             clearTimeout(this.expandProcId);
13660             this.expandProcId = false;
13661         }
13662     },
13663     
13664     isValidDropPoint : function(n, pt, dd, e, data){
13665         if(!n || !data){ return false; }
13666         var targetNode = n.node;
13667         var dropNode = data.node;
13668         // default drop rules
13669         if(!(targetNode && targetNode.isTarget && pt)){
13670             return false;
13671         }
13672         if(pt == "append" && targetNode.allowChildren === false){
13673             return false;
13674         }
13675         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
13676             return false;
13677         }
13678         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
13679             return false;
13680         }
13681         // reuse the object
13682         var overEvent = this.dragOverData;
13683         overEvent.tree = this.tree;
13684         overEvent.target = targetNode;
13685         overEvent.data = data;
13686         overEvent.point = pt;
13687         overEvent.source = dd;
13688         overEvent.rawEvent = e;
13689         overEvent.dropNode = dropNode;
13690         overEvent.cancel = false;  
13691         var result = this.tree.fireEvent("nodedragover", overEvent);
13692         return overEvent.cancel === false && result !== false;
13693     },
13694     
13695     getDropPoint : function(e, n, dd)
13696     {
13697         var tn = n.node;
13698         if(tn.isRoot){
13699             return tn.allowChildren !== false ? "append" : false; // always append for root
13700         }
13701         var dragEl = n.ddel;
13702         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
13703         var y = Roo.lib.Event.getPageY(e);
13704         //var noAppend = tn.allowChildren === false || tn.isLeaf();
13705         
13706         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
13707         var noAppend = tn.allowChildren === false;
13708         if(this.appendOnly || tn.parentNode.allowChildren === false){
13709             return noAppend ? false : "append";
13710         }
13711         var noBelow = false;
13712         if(!this.allowParentInsert){
13713             noBelow = tn.hasChildNodes() && tn.isExpanded();
13714         }
13715         var q = (b - t) / (noAppend ? 2 : 3);
13716         if(y >= t && y < (t + q)){
13717             return "above";
13718         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
13719             return "below";
13720         }else{
13721             return "append";
13722         }
13723     },
13724     
13725     onNodeEnter : function(n, dd, e, data)
13726     {
13727         this.cancelExpand();
13728     },
13729     
13730     onNodeOver : function(n, dd, e, data)
13731     {
13732        
13733         var pt = this.getDropPoint(e, n, dd);
13734         var node = n.node;
13735         
13736         // auto node expand check
13737         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
13738             this.queueExpand(node);
13739         }else if(pt != "append"){
13740             this.cancelExpand();
13741         }
13742         
13743         // set the insert point style on the target node
13744         var returnCls = this.dropNotAllowed;
13745         if(this.isValidDropPoint(n, pt, dd, e, data)){
13746            if(pt){
13747                var el = n.ddel;
13748                var cls;
13749                if(pt == "above"){
13750                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
13751                    cls = "x-tree-drag-insert-above";
13752                }else if(pt == "below"){
13753                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
13754                    cls = "x-tree-drag-insert-below";
13755                }else{
13756                    returnCls = "x-tree-drop-ok-append";
13757                    cls = "x-tree-drag-append";
13758                }
13759                if(this.lastInsertClass != cls){
13760                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
13761                    this.lastInsertClass = cls;
13762                }
13763            }
13764        }
13765        return returnCls;
13766     },
13767     
13768     onNodeOut : function(n, dd, e, data){
13769         
13770         this.cancelExpand();
13771         this.removeDropIndicators(n);
13772     },
13773     
13774     onNodeDrop : function(n, dd, e, data){
13775         var point = this.getDropPoint(e, n, dd);
13776         var targetNode = n.node;
13777         targetNode.ui.startDrop();
13778         if(!this.isValidDropPoint(n, point, dd, e, data)){
13779             targetNode.ui.endDrop();
13780             return false;
13781         }
13782         // first try to find the drop node
13783         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
13784         var dropEvent = {
13785             tree : this.tree,
13786             target: targetNode,
13787             data: data,
13788             point: point,
13789             source: dd,
13790             rawEvent: e,
13791             dropNode: dropNode,
13792             cancel: !dropNode   
13793         };
13794         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
13795         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
13796             targetNode.ui.endDrop();
13797             return false;
13798         }
13799         // allow target changing
13800         targetNode = dropEvent.target;
13801         if(point == "append" && !targetNode.isExpanded()){
13802             targetNode.expand(false, null, function(){
13803                 this.completeDrop(dropEvent);
13804             }.createDelegate(this));
13805         }else{
13806             this.completeDrop(dropEvent);
13807         }
13808         return true;
13809     },
13810     
13811     completeDrop : function(de){
13812         var ns = de.dropNode, p = de.point, t = de.target;
13813         if(!(ns instanceof Array)){
13814             ns = [ns];
13815         }
13816         var n;
13817         for(var i = 0, len = ns.length; i < len; i++){
13818             n = ns[i];
13819             if(p == "above"){
13820                 t.parentNode.insertBefore(n, t);
13821             }else if(p == "below"){
13822                 t.parentNode.insertBefore(n, t.nextSibling);
13823             }else{
13824                 t.appendChild(n);
13825             }
13826         }
13827         n.ui.focus();
13828         if(this.tree.hlDrop){
13829             n.ui.highlight();
13830         }
13831         t.ui.endDrop();
13832         this.tree.fireEvent("nodedrop", de);
13833     },
13834     
13835     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
13836         if(this.tree.hlDrop){
13837             dropNode.ui.focus();
13838             dropNode.ui.highlight();
13839         }
13840         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
13841     },
13842     
13843     getTree : function(){
13844         return this.tree;
13845     },
13846     
13847     removeDropIndicators : function(n){
13848         if(n && n.ddel){
13849             var el = n.ddel;
13850             Roo.fly(el).removeClass([
13851                     "x-tree-drag-insert-above",
13852                     "x-tree-drag-insert-below",
13853                     "x-tree-drag-append"]);
13854             this.lastInsertClass = "_noclass";
13855         }
13856     },
13857     
13858     beforeDragDrop : function(target, e, id){
13859         this.cancelExpand();
13860         return true;
13861     },
13862     
13863     afterRepair : function(data){
13864         if(data && Roo.enableFx){
13865             data.node.ui.highlight();
13866         }
13867         this.hideProxy();
13868     } 
13869     
13870 });
13871
13872 }
13873 /*
13874  * Based on:
13875  * Ext JS Library 1.1.1
13876  * Copyright(c) 2006-2007, Ext JS, LLC.
13877  *
13878  * Originally Released Under LGPL - original licence link has changed is not relivant.
13879  *
13880  * Fork - LGPL
13881  * <script type="text/javascript">
13882  */
13883  
13884
13885 if(Roo.dd.DragZone){
13886 Roo.tree.TreeDragZone = function(tree, config){
13887     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
13888     this.tree = tree;
13889 };
13890
13891 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
13892     ddGroup : "TreeDD",
13893    
13894     onBeforeDrag : function(data, e){
13895         var n = data.node;
13896         return n && n.draggable && !n.disabled;
13897     },
13898      
13899     
13900     onInitDrag : function(e){
13901         var data = this.dragData;
13902         this.tree.getSelectionModel().select(data.node);
13903         this.proxy.update("");
13904         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
13905         this.tree.fireEvent("startdrag", this.tree, data.node, e);
13906     },
13907     
13908     getRepairXY : function(e, data){
13909         return data.node.ui.getDDRepairXY();
13910     },
13911     
13912     onEndDrag : function(data, e){
13913         this.tree.fireEvent("enddrag", this.tree, data.node, e);
13914         
13915         
13916     },
13917     
13918     onValidDrop : function(dd, e, id){
13919         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
13920         this.hideProxy();
13921     },
13922     
13923     beforeInvalidDrop : function(e, id){
13924         // this scrolls the original position back into view
13925         var sm = this.tree.getSelectionModel();
13926         sm.clearSelections();
13927         sm.select(this.dragData.node);
13928     }
13929 });
13930 }/*
13931  * Based on:
13932  * Ext JS Library 1.1.1
13933  * Copyright(c) 2006-2007, Ext JS, LLC.
13934  *
13935  * Originally Released Under LGPL - original licence link has changed is not relivant.
13936  *
13937  * Fork - LGPL
13938  * <script type="text/javascript">
13939  */
13940 /**
13941  * @class Roo.tree.TreeEditor
13942  * @extends Roo.Editor
13943  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
13944  * as the editor field.
13945  * @constructor
13946  * @param {Object} config (used to be the tree panel.)
13947  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
13948  * 
13949  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
13950  * @cfg {Roo.form.TextField|Object} field The field configuration
13951  *
13952  * 
13953  */
13954 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
13955     var tree = config;
13956     var field;
13957     if (oldconfig) { // old style..
13958         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
13959     } else {
13960         // new style..
13961         tree = config.tree;
13962         config.field = config.field  || {};
13963         config.field.xtype = 'TextField';
13964         field = Roo.factory(config.field, Roo.form);
13965     }
13966     config = config || {};
13967     
13968     
13969     this.addEvents({
13970         /**
13971          * @event beforenodeedit
13972          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
13973          * false from the handler of this event.
13974          * @param {Editor} this
13975          * @param {Roo.tree.Node} node 
13976          */
13977         "beforenodeedit" : true
13978     });
13979     
13980     //Roo.log(config);
13981     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
13982
13983     this.tree = tree;
13984
13985     tree.on('beforeclick', this.beforeNodeClick, this);
13986     tree.getTreeEl().on('mousedown', this.hide, this);
13987     this.on('complete', this.updateNode, this);
13988     this.on('beforestartedit', this.fitToTree, this);
13989     this.on('startedit', this.bindScroll, this, {delay:10});
13990     this.on('specialkey', this.onSpecialKey, this);
13991 };
13992
13993 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
13994     /**
13995      * @cfg {String} alignment
13996      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
13997      */
13998     alignment: "l-l",
13999     // inherit
14000     autoSize: false,
14001     /**
14002      * @cfg {Boolean} hideEl
14003      * True to hide the bound element while the editor is displayed (defaults to false)
14004      */
14005     hideEl : false,
14006     /**
14007      * @cfg {String} cls
14008      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
14009      */
14010     cls: "x-small-editor x-tree-editor",
14011     /**
14012      * @cfg {Boolean} shim
14013      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
14014      */
14015     shim:false,
14016     // inherit
14017     shadow:"frame",
14018     /**
14019      * @cfg {Number} maxWidth
14020      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
14021      * the containing tree element's size, it will be automatically limited for you to the container width, taking
14022      * scroll and client offsets into account prior to each edit.
14023      */
14024     maxWidth: 250,
14025
14026     editDelay : 350,
14027
14028     // private
14029     fitToTree : function(ed, el){
14030         var td = this.tree.getTreeEl().dom, nd = el.dom;
14031         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
14032             td.scrollLeft = nd.offsetLeft;
14033         }
14034         var w = Math.min(
14035                 this.maxWidth,
14036                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
14037         this.setSize(w, '');
14038         
14039         return this.fireEvent('beforenodeedit', this, this.editNode);
14040         
14041     },
14042
14043     // private
14044     triggerEdit : function(node){
14045         this.completeEdit();
14046         this.editNode = node;
14047         this.startEdit(node.ui.textNode, node.text);
14048     },
14049
14050     // private
14051     bindScroll : function(){
14052         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
14053     },
14054
14055     // private
14056     beforeNodeClick : function(node, e){
14057         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
14058         this.lastClick = new Date();
14059         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
14060             e.stopEvent();
14061             this.triggerEdit(node);
14062             return false;
14063         }
14064         return true;
14065     },
14066
14067     // private
14068     updateNode : function(ed, value){
14069         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
14070         this.editNode.setText(value);
14071     },
14072
14073     // private
14074     onHide : function(){
14075         Roo.tree.TreeEditor.superclass.onHide.call(this);
14076         if(this.editNode){
14077             this.editNode.ui.focus();
14078         }
14079     },
14080
14081     // private
14082     onSpecialKey : function(field, e){
14083         var k = e.getKey();
14084         if(k == e.ESC){
14085             e.stopEvent();
14086             this.cancelEdit();
14087         }else if(k == e.ENTER && !e.hasModifier()){
14088             e.stopEvent();
14089             this.completeEdit();
14090         }
14091     }
14092 });//<Script type="text/javascript">
14093 /*
14094  * Based on:
14095  * Ext JS Library 1.1.1
14096  * Copyright(c) 2006-2007, Ext JS, LLC.
14097  *
14098  * Originally Released Under LGPL - original licence link has changed is not relivant.
14099  *
14100  * Fork - LGPL
14101  * <script type="text/javascript">
14102  */
14103  
14104 /**
14105  * Not documented??? - probably should be...
14106  */
14107
14108 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
14109     //focus: Roo.emptyFn, // prevent odd scrolling behavior
14110     
14111     renderElements : function(n, a, targetNode, bulkRender){
14112         //consel.log("renderElements?");
14113         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
14114
14115         var t = n.getOwnerTree();
14116         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
14117         
14118         var cols = t.columns;
14119         var bw = t.borderWidth;
14120         var c = cols[0];
14121         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
14122          var cb = typeof a.checked == "boolean";
14123         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14124         var colcls = 'x-t-' + tid + '-c0';
14125         var buf = [
14126             '<li class="x-tree-node">',
14127             
14128                 
14129                 '<div class="x-tree-node-el ', a.cls,'">',
14130                     // extran...
14131                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
14132                 
14133                 
14134                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
14135                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
14136                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
14137                            (a.icon ? ' x-tree-node-inline-icon' : ''),
14138                            (a.iconCls ? ' '+a.iconCls : ''),
14139                            '" unselectable="on" />',
14140                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
14141                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
14142                              
14143                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14144                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
14145                             '<span unselectable="on" qtip="' + tx + '">',
14146                              tx,
14147                              '</span></a>' ,
14148                     '</div>',
14149                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
14150                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
14151                  ];
14152         for(var i = 1, len = cols.length; i < len; i++){
14153             c = cols[i];
14154             colcls = 'x-t-' + tid + '-c' +i;
14155             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
14156             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
14157                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
14158                       "</div>");
14159          }
14160          
14161          buf.push(
14162             '</a>',
14163             '<div class="x-clear"></div></div>',
14164             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
14165             "</li>");
14166         
14167         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
14168             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
14169                                 n.nextSibling.ui.getEl(), buf.join(""));
14170         }else{
14171             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
14172         }
14173         var el = this.wrap.firstChild;
14174         this.elRow = el;
14175         this.elNode = el.firstChild;
14176         this.ranchor = el.childNodes[1];
14177         this.ctNode = this.wrap.childNodes[1];
14178         var cs = el.firstChild.childNodes;
14179         this.indentNode = cs[0];
14180         this.ecNode = cs[1];
14181         this.iconNode = cs[2];
14182         var index = 3;
14183         if(cb){
14184             this.checkbox = cs[3];
14185             index++;
14186         }
14187         this.anchor = cs[index];
14188         
14189         this.textNode = cs[index].firstChild;
14190         
14191         //el.on("click", this.onClick, this);
14192         //el.on("dblclick", this.onDblClick, this);
14193         
14194         
14195        // console.log(this);
14196     },
14197     initEvents : function(){
14198         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
14199         
14200             
14201         var a = this.ranchor;
14202
14203         var el = Roo.get(a);
14204
14205         if(Roo.isOpera){ // opera render bug ignores the CSS
14206             el.setStyle("text-decoration", "none");
14207         }
14208
14209         el.on("click", this.onClick, this);
14210         el.on("dblclick", this.onDblClick, this);
14211         el.on("contextmenu", this.onContextMenu, this);
14212         
14213     },
14214     
14215     /*onSelectedChange : function(state){
14216         if(state){
14217             this.focus();
14218             this.addClass("x-tree-selected");
14219         }else{
14220             //this.blur();
14221             this.removeClass("x-tree-selected");
14222         }
14223     },*/
14224     addClass : function(cls){
14225         if(this.elRow){
14226             Roo.fly(this.elRow).addClass(cls);
14227         }
14228         
14229     },
14230     
14231     
14232     removeClass : function(cls){
14233         if(this.elRow){
14234             Roo.fly(this.elRow).removeClass(cls);
14235         }
14236     }
14237
14238     
14239     
14240 });//<Script type="text/javascript">
14241
14242 /*
14243  * Based on:
14244  * Ext JS Library 1.1.1
14245  * Copyright(c) 2006-2007, Ext JS, LLC.
14246  *
14247  * Originally Released Under LGPL - original licence link has changed is not relivant.
14248  *
14249  * Fork - LGPL
14250  * <script type="text/javascript">
14251  */
14252  
14253
14254 /**
14255  * @class Roo.tree.ColumnTree
14256  * @extends Roo.data.TreePanel
14257  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
14258  * @cfg {int} borderWidth  compined right/left border allowance
14259  * @constructor
14260  * @param {String/HTMLElement/Element} el The container element
14261  * @param {Object} config
14262  */
14263 Roo.tree.ColumnTree =  function(el, config)
14264 {
14265    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
14266    this.addEvents({
14267         /**
14268         * @event resize
14269         * Fire this event on a container when it resizes
14270         * @param {int} w Width
14271         * @param {int} h Height
14272         */
14273        "resize" : true
14274     });
14275     this.on('resize', this.onResize, this);
14276 };
14277
14278 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
14279     //lines:false,
14280     
14281     
14282     borderWidth: Roo.isBorderBox ? 0 : 2, 
14283     headEls : false,
14284     
14285     render : function(){
14286         // add the header.....
14287        
14288         Roo.tree.ColumnTree.superclass.render.apply(this);
14289         
14290         this.el.addClass('x-column-tree');
14291         
14292         this.headers = this.el.createChild(
14293             {cls:'x-tree-headers'},this.innerCt.dom);
14294    
14295         var cols = this.columns, c;
14296         var totalWidth = 0;
14297         this.headEls = [];
14298         var  len = cols.length;
14299         for(var i = 0; i < len; i++){
14300              c = cols[i];
14301              totalWidth += c.width;
14302             this.headEls.push(this.headers.createChild({
14303                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
14304                  cn: {
14305                      cls:'x-tree-hd-text',
14306                      html: c.header
14307                  },
14308                  style:'width:'+(c.width-this.borderWidth)+'px;'
14309              }));
14310         }
14311         this.headers.createChild({cls:'x-clear'});
14312         // prevent floats from wrapping when clipped
14313         this.headers.setWidth(totalWidth);
14314         //this.innerCt.setWidth(totalWidth);
14315         this.innerCt.setStyle({ overflow: 'auto' });
14316         this.onResize(this.width, this.height);
14317              
14318         
14319     },
14320     onResize : function(w,h)
14321     {
14322         this.height = h;
14323         this.width = w;
14324         // resize cols..
14325         this.innerCt.setWidth(this.width);
14326         this.innerCt.setHeight(this.height-20);
14327         
14328         // headers...
14329         var cols = this.columns, c;
14330         var totalWidth = 0;
14331         var expEl = false;
14332         var len = cols.length;
14333         for(var i = 0; i < len; i++){
14334             c = cols[i];
14335             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
14336                 // it's the expander..
14337                 expEl  = this.headEls[i];
14338                 continue;
14339             }
14340             totalWidth += c.width;
14341             
14342         }
14343         if (expEl) {
14344             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
14345         }
14346         this.headers.setWidth(w-20);
14347
14348         
14349         
14350         
14351     }
14352 });
14353 /*
14354  * Based on:
14355  * Ext JS Library 1.1.1
14356  * Copyright(c) 2006-2007, Ext JS, LLC.
14357  *
14358  * Originally Released Under LGPL - original licence link has changed is not relivant.
14359  *
14360  * Fork - LGPL
14361  * <script type="text/javascript">
14362  */
14363  
14364 /**
14365  * @class Roo.menu.Menu
14366  * @extends Roo.util.Observable
14367  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
14368  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
14369  * @constructor
14370  * Creates a new Menu
14371  * @param {Object} config Configuration options
14372  */
14373 Roo.menu.Menu = function(config){
14374     
14375     Roo.menu.Menu.superclass.constructor.call(this, config);
14376     
14377     this.id = this.id || Roo.id();
14378     this.addEvents({
14379         /**
14380          * @event beforeshow
14381          * Fires before this menu is displayed
14382          * @param {Roo.menu.Menu} this
14383          */
14384         beforeshow : true,
14385         /**
14386          * @event beforehide
14387          * Fires before this menu is hidden
14388          * @param {Roo.menu.Menu} this
14389          */
14390         beforehide : true,
14391         /**
14392          * @event show
14393          * Fires after this menu is displayed
14394          * @param {Roo.menu.Menu} this
14395          */
14396         show : true,
14397         /**
14398          * @event hide
14399          * Fires after this menu is hidden
14400          * @param {Roo.menu.Menu} this
14401          */
14402         hide : true,
14403         /**
14404          * @event click
14405          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
14406          * @param {Roo.menu.Menu} this
14407          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14408          * @param {Roo.EventObject} e
14409          */
14410         click : true,
14411         /**
14412          * @event mouseover
14413          * Fires when the mouse is hovering over this menu
14414          * @param {Roo.menu.Menu} this
14415          * @param {Roo.EventObject} e
14416          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14417          */
14418         mouseover : true,
14419         /**
14420          * @event mouseout
14421          * Fires when the mouse exits this menu
14422          * @param {Roo.menu.Menu} this
14423          * @param {Roo.EventObject} e
14424          * @param {Roo.menu.Item} menuItem The menu item that was clicked
14425          */
14426         mouseout : true,
14427         /**
14428          * @event itemclick
14429          * Fires when a menu item contained in this menu is clicked
14430          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
14431          * @param {Roo.EventObject} e
14432          */
14433         itemclick: true
14434     });
14435     if (this.registerMenu) {
14436         Roo.menu.MenuMgr.register(this);
14437     }
14438     
14439     var mis = this.items;
14440     this.items = new Roo.util.MixedCollection();
14441     if(mis){
14442         this.add.apply(this, mis);
14443     }
14444 };
14445
14446 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
14447     /**
14448      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
14449      */
14450     minWidth : 120,
14451     /**
14452      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
14453      * for bottom-right shadow (defaults to "sides")
14454      */
14455     shadow : "sides",
14456     /**
14457      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
14458      * this menu (defaults to "tl-tr?")
14459      */
14460     subMenuAlign : "tl-tr?",
14461     /**
14462      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
14463      * relative to its element of origin (defaults to "tl-bl?")
14464      */
14465     defaultAlign : "tl-bl?",
14466     /**
14467      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
14468      */
14469     allowOtherMenus : false,
14470     /**
14471      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
14472      */
14473     registerMenu : true,
14474
14475     hidden:true,
14476
14477     // private
14478     render : function(){
14479         if(this.el){
14480             return;
14481         }
14482         var el = this.el = new Roo.Layer({
14483             cls: "x-menu",
14484             shadow:this.shadow,
14485             constrain: false,
14486             parentEl: this.parentEl || document.body,
14487             zindex:15000
14488         });
14489
14490         this.keyNav = new Roo.menu.MenuNav(this);
14491
14492         if(this.plain){
14493             el.addClass("x-menu-plain");
14494         }
14495         if(this.cls){
14496             el.addClass(this.cls);
14497         }
14498         // generic focus element
14499         this.focusEl = el.createChild({
14500             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
14501         });
14502         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
14503         //disabling touch- as it's causing issues ..
14504         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
14505         ul.on('click'   , this.onClick, this);
14506         
14507         
14508         ul.on("mouseover", this.onMouseOver, this);
14509         ul.on("mouseout", this.onMouseOut, this);
14510         this.items.each(function(item){
14511             if (item.hidden) {
14512                 return;
14513             }
14514             
14515             var li = document.createElement("li");
14516             li.className = "x-menu-list-item";
14517             ul.dom.appendChild(li);
14518             item.render(li, this);
14519         }, this);
14520         this.ul = ul;
14521         this.autoWidth();
14522     },
14523
14524     // private
14525     autoWidth : function(){
14526         var el = this.el, ul = this.ul;
14527         if(!el){
14528             return;
14529         }
14530         var w = this.width;
14531         if(w){
14532             el.setWidth(w);
14533         }else if(Roo.isIE){
14534             el.setWidth(this.minWidth);
14535             var t = el.dom.offsetWidth; // force recalc
14536             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
14537         }
14538     },
14539
14540     // private
14541     delayAutoWidth : function(){
14542         if(this.rendered){
14543             if(!this.awTask){
14544                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
14545             }
14546             this.awTask.delay(20);
14547         }
14548     },
14549
14550     // private
14551     findTargetItem : function(e){
14552         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
14553         if(t && t.menuItemId){
14554             return this.items.get(t.menuItemId);
14555         }
14556     },
14557
14558     // private
14559     onClick : function(e){
14560         Roo.log("menu.onClick");
14561         var t = this.findTargetItem(e);
14562         if(!t){
14563             return;
14564         }
14565         Roo.log(e);
14566         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
14567             if(t == this.activeItem && t.shouldDeactivate(e)){
14568                 this.activeItem.deactivate();
14569                 delete this.activeItem;
14570                 return;
14571             }
14572             if(t.canActivate){
14573                 this.setActiveItem(t, true);
14574             }
14575             return;
14576             
14577             
14578         }
14579         
14580         t.onClick(e);
14581         this.fireEvent("click", this, t, e);
14582     },
14583
14584     // private
14585     setActiveItem : function(item, autoExpand){
14586         if(item != this.activeItem){
14587             if(this.activeItem){
14588                 this.activeItem.deactivate();
14589             }
14590             this.activeItem = item;
14591             item.activate(autoExpand);
14592         }else if(autoExpand){
14593             item.expandMenu();
14594         }
14595     },
14596
14597     // private
14598     tryActivate : function(start, step){
14599         var items = this.items;
14600         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
14601             var item = items.get(i);
14602             if(!item.disabled && item.canActivate){
14603                 this.setActiveItem(item, false);
14604                 return item;
14605             }
14606         }
14607         return false;
14608     },
14609
14610     // private
14611     onMouseOver : function(e){
14612         var t;
14613         if(t = this.findTargetItem(e)){
14614             if(t.canActivate && !t.disabled){
14615                 this.setActiveItem(t, true);
14616             }
14617         }
14618         this.fireEvent("mouseover", this, e, t);
14619     },
14620
14621     // private
14622     onMouseOut : function(e){
14623         var t;
14624         if(t = this.findTargetItem(e)){
14625             if(t == this.activeItem && t.shouldDeactivate(e)){
14626                 this.activeItem.deactivate();
14627                 delete this.activeItem;
14628             }
14629         }
14630         this.fireEvent("mouseout", this, e, t);
14631     },
14632
14633     /**
14634      * Read-only.  Returns true if the menu is currently displayed, else false.
14635      * @type Boolean
14636      */
14637     isVisible : function(){
14638         return this.el && !this.hidden;
14639     },
14640
14641     /**
14642      * Displays this menu relative to another element
14643      * @param {String/HTMLElement/Roo.Element} element The element to align to
14644      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
14645      * the element (defaults to this.defaultAlign)
14646      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14647      */
14648     show : function(el, pos, parentMenu){
14649         this.parentMenu = parentMenu;
14650         if(!this.el){
14651             this.render();
14652         }
14653         this.fireEvent("beforeshow", this);
14654         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
14655     },
14656
14657     /**
14658      * Displays this menu at a specific xy position
14659      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
14660      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
14661      */
14662     showAt : function(xy, parentMenu, /* private: */_e){
14663         this.parentMenu = parentMenu;
14664         if(!this.el){
14665             this.render();
14666         }
14667         if(_e !== false){
14668             this.fireEvent("beforeshow", this);
14669             xy = this.el.adjustForConstraints(xy);
14670         }
14671         this.el.setXY(xy);
14672         this.el.show();
14673         this.hidden = false;
14674         this.focus();
14675         this.fireEvent("show", this);
14676     },
14677
14678     focus : function(){
14679         if(!this.hidden){
14680             this.doFocus.defer(50, this);
14681         }
14682     },
14683
14684     doFocus : function(){
14685         if(!this.hidden){
14686             this.focusEl.focus();
14687         }
14688     },
14689
14690     /**
14691      * Hides this menu and optionally all parent menus
14692      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
14693      */
14694     hide : function(deep){
14695         if(this.el && this.isVisible()){
14696             this.fireEvent("beforehide", this);
14697             if(this.activeItem){
14698                 this.activeItem.deactivate();
14699                 this.activeItem = null;
14700             }
14701             this.el.hide();
14702             this.hidden = true;
14703             this.fireEvent("hide", this);
14704         }
14705         if(deep === true && this.parentMenu){
14706             this.parentMenu.hide(true);
14707         }
14708     },
14709
14710     /**
14711      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
14712      * Any of the following are valid:
14713      * <ul>
14714      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
14715      * <li>An HTMLElement object which will be converted to a menu item</li>
14716      * <li>A menu item config object that will be created as a new menu item</li>
14717      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
14718      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
14719      * </ul>
14720      * Usage:
14721      * <pre><code>
14722 // Create the menu
14723 var menu = new Roo.menu.Menu();
14724
14725 // Create a menu item to add by reference
14726 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
14727
14728 // Add a bunch of items at once using different methods.
14729 // Only the last item added will be returned.
14730 var item = menu.add(
14731     menuItem,                // add existing item by ref
14732     'Dynamic Item',          // new TextItem
14733     '-',                     // new separator
14734     { text: 'Config Item' }  // new item by config
14735 );
14736 </code></pre>
14737      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
14738      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
14739      */
14740     add : function(){
14741         var a = arguments, l = a.length, item;
14742         for(var i = 0; i < l; i++){
14743             var el = a[i];
14744             if ((typeof(el) == "object") && el.xtype && el.xns) {
14745                 el = Roo.factory(el, Roo.menu);
14746             }
14747             
14748             if(el.render){ // some kind of Item
14749                 item = this.addItem(el);
14750             }else if(typeof el == "string"){ // string
14751                 if(el == "separator" || el == "-"){
14752                     item = this.addSeparator();
14753                 }else{
14754                     item = this.addText(el);
14755                 }
14756             }else if(el.tagName || el.el){ // element
14757                 item = this.addElement(el);
14758             }else if(typeof el == "object"){ // must be menu item config?
14759                 item = this.addMenuItem(el);
14760             }
14761         }
14762         return item;
14763     },
14764
14765     /**
14766      * Returns this menu's underlying {@link Roo.Element} object
14767      * @return {Roo.Element} The element
14768      */
14769     getEl : function(){
14770         if(!this.el){
14771             this.render();
14772         }
14773         return this.el;
14774     },
14775
14776     /**
14777      * Adds a separator bar to the menu
14778      * @return {Roo.menu.Item} The menu item that was added
14779      */
14780     addSeparator : function(){
14781         return this.addItem(new Roo.menu.Separator());
14782     },
14783
14784     /**
14785      * Adds an {@link Roo.Element} object to the menu
14786      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
14787      * @return {Roo.menu.Item} The menu item that was added
14788      */
14789     addElement : function(el){
14790         return this.addItem(new Roo.menu.BaseItem(el));
14791     },
14792
14793     /**
14794      * Adds an existing object based on {@link Roo.menu.Item} to the menu
14795      * @param {Roo.menu.Item} item The menu item to add
14796      * @return {Roo.menu.Item} The menu item that was added
14797      */
14798     addItem : function(item){
14799         this.items.add(item);
14800         if(this.ul){
14801             var li = document.createElement("li");
14802             li.className = "x-menu-list-item";
14803             this.ul.dom.appendChild(li);
14804             item.render(li, this);
14805             this.delayAutoWidth();
14806         }
14807         return item;
14808     },
14809
14810     /**
14811      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
14812      * @param {Object} config A MenuItem config object
14813      * @return {Roo.menu.Item} The menu item that was added
14814      */
14815     addMenuItem : function(config){
14816         if(!(config instanceof Roo.menu.Item)){
14817             if(typeof config.checked == "boolean"){ // must be check menu item config?
14818                 config = new Roo.menu.CheckItem(config);
14819             }else{
14820                 config = new Roo.menu.Item(config);
14821             }
14822         }
14823         return this.addItem(config);
14824     },
14825
14826     /**
14827      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
14828      * @param {String} text The text to display in the menu item
14829      * @return {Roo.menu.Item} The menu item that was added
14830      */
14831     addText : function(text){
14832         return this.addItem(new Roo.menu.TextItem({ text : text }));
14833     },
14834
14835     /**
14836      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
14837      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
14838      * @param {Roo.menu.Item} item The menu item to add
14839      * @return {Roo.menu.Item} The menu item that was added
14840      */
14841     insert : function(index, item){
14842         this.items.insert(index, item);
14843         if(this.ul){
14844             var li = document.createElement("li");
14845             li.className = "x-menu-list-item";
14846             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
14847             item.render(li, this);
14848             this.delayAutoWidth();
14849         }
14850         return item;
14851     },
14852
14853     /**
14854      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
14855      * @param {Roo.menu.Item} item The menu item to remove
14856      */
14857     remove : function(item){
14858         this.items.removeKey(item.id);
14859         item.destroy();
14860     },
14861
14862     /**
14863      * Removes and destroys all items in the menu
14864      */
14865     removeAll : function(){
14866         var f;
14867         while(f = this.items.first()){
14868             this.remove(f);
14869         }
14870     }
14871 });
14872
14873 // MenuNav is a private utility class used internally by the Menu
14874 Roo.menu.MenuNav = function(menu){
14875     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
14876     this.scope = this.menu = menu;
14877 };
14878
14879 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
14880     doRelay : function(e, h){
14881         var k = e.getKey();
14882         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
14883             this.menu.tryActivate(0, 1);
14884             return false;
14885         }
14886         return h.call(this.scope || this, e, this.menu);
14887     },
14888
14889     up : function(e, m){
14890         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
14891             m.tryActivate(m.items.length-1, -1);
14892         }
14893     },
14894
14895     down : function(e, m){
14896         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
14897             m.tryActivate(0, 1);
14898         }
14899     },
14900
14901     right : function(e, m){
14902         if(m.activeItem){
14903             m.activeItem.expandMenu(true);
14904         }
14905     },
14906
14907     left : function(e, m){
14908         m.hide();
14909         if(m.parentMenu && m.parentMenu.activeItem){
14910             m.parentMenu.activeItem.activate();
14911         }
14912     },
14913
14914     enter : function(e, m){
14915         if(m.activeItem){
14916             e.stopPropagation();
14917             m.activeItem.onClick(e);
14918             m.fireEvent("click", this, m.activeItem);
14919             return true;
14920         }
14921     }
14922 });/*
14923  * Based on:
14924  * Ext JS Library 1.1.1
14925  * Copyright(c) 2006-2007, Ext JS, LLC.
14926  *
14927  * Originally Released Under LGPL - original licence link has changed is not relivant.
14928  *
14929  * Fork - LGPL
14930  * <script type="text/javascript">
14931  */
14932  
14933 /**
14934  * @class Roo.menu.MenuMgr
14935  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
14936  * @singleton
14937  */
14938 Roo.menu.MenuMgr = function(){
14939    var menus, active, groups = {}, attached = false, lastShow = new Date();
14940
14941    // private - called when first menu is created
14942    function init(){
14943        menus = {};
14944        active = new Roo.util.MixedCollection();
14945        Roo.get(document).addKeyListener(27, function(){
14946            if(active.length > 0){
14947                hideAll();
14948            }
14949        });
14950    }
14951
14952    // private
14953    function hideAll(){
14954        if(active && active.length > 0){
14955            var c = active.clone();
14956            c.each(function(m){
14957                m.hide();
14958            });
14959        }
14960    }
14961
14962    // private
14963    function onHide(m){
14964        active.remove(m);
14965        if(active.length < 1){
14966            Roo.get(document).un("mousedown", onMouseDown);
14967            attached = false;
14968        }
14969    }
14970
14971    // private
14972    function onShow(m){
14973        var last = active.last();
14974        lastShow = new Date();
14975        active.add(m);
14976        if(!attached){
14977            Roo.get(document).on("mousedown", onMouseDown);
14978            attached = true;
14979        }
14980        if(m.parentMenu){
14981           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
14982           m.parentMenu.activeChild = m;
14983        }else if(last && last.isVisible()){
14984           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
14985        }
14986    }
14987
14988    // private
14989    function onBeforeHide(m){
14990        if(m.activeChild){
14991            m.activeChild.hide();
14992        }
14993        if(m.autoHideTimer){
14994            clearTimeout(m.autoHideTimer);
14995            delete m.autoHideTimer;
14996        }
14997    }
14998
14999    // private
15000    function onBeforeShow(m){
15001        var pm = m.parentMenu;
15002        if(!pm && !m.allowOtherMenus){
15003            hideAll();
15004        }else if(pm && pm.activeChild && active != m){
15005            pm.activeChild.hide();
15006        }
15007    }
15008
15009    // private
15010    function onMouseDown(e){
15011        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
15012            hideAll();
15013        }
15014    }
15015
15016    // private
15017    function onBeforeCheck(mi, state){
15018        if(state){
15019            var g = groups[mi.group];
15020            for(var i = 0, l = g.length; i < l; i++){
15021                if(g[i] != mi){
15022                    g[i].setChecked(false);
15023                }
15024            }
15025        }
15026    }
15027
15028    return {
15029
15030        /**
15031         * Hides all menus that are currently visible
15032         */
15033        hideAll : function(){
15034             hideAll();  
15035        },
15036
15037        // private
15038        register : function(menu){
15039            if(!menus){
15040                init();
15041            }
15042            menus[menu.id] = menu;
15043            menu.on("beforehide", onBeforeHide);
15044            menu.on("hide", onHide);
15045            menu.on("beforeshow", onBeforeShow);
15046            menu.on("show", onShow);
15047            var g = menu.group;
15048            if(g && menu.events["checkchange"]){
15049                if(!groups[g]){
15050                    groups[g] = [];
15051                }
15052                groups[g].push(menu);
15053                menu.on("checkchange", onCheck);
15054            }
15055        },
15056
15057         /**
15058          * Returns a {@link Roo.menu.Menu} object
15059          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
15060          * be used to generate and return a new Menu instance.
15061          */
15062        get : function(menu){
15063            if(typeof menu == "string"){ // menu id
15064                return menus[menu];
15065            }else if(menu.events){  // menu instance
15066                return menu;
15067            }else if(typeof menu.length == 'number'){ // array of menu items?
15068                return new Roo.menu.Menu({items:menu});
15069            }else{ // otherwise, must be a config
15070                return new Roo.menu.Menu(menu);
15071            }
15072        },
15073
15074        // private
15075        unregister : function(menu){
15076            delete menus[menu.id];
15077            menu.un("beforehide", onBeforeHide);
15078            menu.un("hide", onHide);
15079            menu.un("beforeshow", onBeforeShow);
15080            menu.un("show", onShow);
15081            var g = menu.group;
15082            if(g && menu.events["checkchange"]){
15083                groups[g].remove(menu);
15084                menu.un("checkchange", onCheck);
15085            }
15086        },
15087
15088        // private
15089        registerCheckable : function(menuItem){
15090            var g = menuItem.group;
15091            if(g){
15092                if(!groups[g]){
15093                    groups[g] = [];
15094                }
15095                groups[g].push(menuItem);
15096                menuItem.on("beforecheckchange", onBeforeCheck);
15097            }
15098        },
15099
15100        // private
15101        unregisterCheckable : function(menuItem){
15102            var g = menuItem.group;
15103            if(g){
15104                groups[g].remove(menuItem);
15105                menuItem.un("beforecheckchange", onBeforeCheck);
15106            }
15107        }
15108    };
15109 }();/*
15110  * Based on:
15111  * Ext JS Library 1.1.1
15112  * Copyright(c) 2006-2007, Ext JS, LLC.
15113  *
15114  * Originally Released Under LGPL - original licence link has changed is not relivant.
15115  *
15116  * Fork - LGPL
15117  * <script type="text/javascript">
15118  */
15119  
15120
15121 /**
15122  * @class Roo.menu.BaseItem
15123  * @extends Roo.Component
15124  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
15125  * management and base configuration options shared by all menu components.
15126  * @constructor
15127  * Creates a new BaseItem
15128  * @param {Object} config Configuration options
15129  */
15130 Roo.menu.BaseItem = function(config){
15131     Roo.menu.BaseItem.superclass.constructor.call(this, config);
15132
15133     this.addEvents({
15134         /**
15135          * @event click
15136          * Fires when this item is clicked
15137          * @param {Roo.menu.BaseItem} this
15138          * @param {Roo.EventObject} e
15139          */
15140         click: true,
15141         /**
15142          * @event activate
15143          * Fires when this item is activated
15144          * @param {Roo.menu.BaseItem} this
15145          */
15146         activate : true,
15147         /**
15148          * @event deactivate
15149          * Fires when this item is deactivated
15150          * @param {Roo.menu.BaseItem} this
15151          */
15152         deactivate : true
15153     });
15154
15155     if(this.handler){
15156         this.on("click", this.handler, this.scope, true);
15157     }
15158 };
15159
15160 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
15161     /**
15162      * @cfg {Function} handler
15163      * A function that will handle the click event of this menu item (defaults to undefined)
15164      */
15165     /**
15166      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
15167      */
15168     canActivate : false,
15169     
15170      /**
15171      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
15172      */
15173     hidden: false,
15174     
15175     /**
15176      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
15177      */
15178     activeClass : "x-menu-item-active",
15179     /**
15180      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
15181      */
15182     hideOnClick : true,
15183     /**
15184      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
15185      */
15186     hideDelay : 100,
15187
15188     // private
15189     ctype: "Roo.menu.BaseItem",
15190
15191     // private
15192     actionMode : "container",
15193
15194     // private
15195     render : function(container, parentMenu){
15196         this.parentMenu = parentMenu;
15197         Roo.menu.BaseItem.superclass.render.call(this, container);
15198         this.container.menuItemId = this.id;
15199     },
15200
15201     // private
15202     onRender : function(container, position){
15203         this.el = Roo.get(this.el);
15204         container.dom.appendChild(this.el.dom);
15205     },
15206
15207     // private
15208     onClick : function(e){
15209         if(!this.disabled && this.fireEvent("click", this, e) !== false
15210                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
15211             this.handleClick(e);
15212         }else{
15213             e.stopEvent();
15214         }
15215     },
15216
15217     // private
15218     activate : function(){
15219         if(this.disabled){
15220             return false;
15221         }
15222         var li = this.container;
15223         li.addClass(this.activeClass);
15224         this.region = li.getRegion().adjust(2, 2, -2, -2);
15225         this.fireEvent("activate", this);
15226         return true;
15227     },
15228
15229     // private
15230     deactivate : function(){
15231         this.container.removeClass(this.activeClass);
15232         this.fireEvent("deactivate", this);
15233     },
15234
15235     // private
15236     shouldDeactivate : function(e){
15237         return !this.region || !this.region.contains(e.getPoint());
15238     },
15239
15240     // private
15241     handleClick : function(e){
15242         if(this.hideOnClick){
15243             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
15244         }
15245     },
15246
15247     // private
15248     expandMenu : function(autoActivate){
15249         // do nothing
15250     },
15251
15252     // private
15253     hideMenu : function(){
15254         // do nothing
15255     }
15256 });/*
15257  * Based on:
15258  * Ext JS Library 1.1.1
15259  * Copyright(c) 2006-2007, Ext JS, LLC.
15260  *
15261  * Originally Released Under LGPL - original licence link has changed is not relivant.
15262  *
15263  * Fork - LGPL
15264  * <script type="text/javascript">
15265  */
15266  
15267 /**
15268  * @class Roo.menu.Adapter
15269  * @extends Roo.menu.BaseItem
15270  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
15271  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
15272  * @constructor
15273  * Creates a new Adapter
15274  * @param {Object} config Configuration options
15275  */
15276 Roo.menu.Adapter = function(component, config){
15277     Roo.menu.Adapter.superclass.constructor.call(this, config);
15278     this.component = component;
15279 };
15280 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
15281     // private
15282     canActivate : true,
15283
15284     // private
15285     onRender : function(container, position){
15286         this.component.render(container);
15287         this.el = this.component.getEl();
15288     },
15289
15290     // private
15291     activate : function(){
15292         if(this.disabled){
15293             return false;
15294         }
15295         this.component.focus();
15296         this.fireEvent("activate", this);
15297         return true;
15298     },
15299
15300     // private
15301     deactivate : function(){
15302         this.fireEvent("deactivate", this);
15303     },
15304
15305     // private
15306     disable : function(){
15307         this.component.disable();
15308         Roo.menu.Adapter.superclass.disable.call(this);
15309     },
15310
15311     // private
15312     enable : function(){
15313         this.component.enable();
15314         Roo.menu.Adapter.superclass.enable.call(this);
15315     }
15316 });/*
15317  * Based on:
15318  * Ext JS Library 1.1.1
15319  * Copyright(c) 2006-2007, Ext JS, LLC.
15320  *
15321  * Originally Released Under LGPL - original licence link has changed is not relivant.
15322  *
15323  * Fork - LGPL
15324  * <script type="text/javascript">
15325  */
15326
15327 /**
15328  * @class Roo.menu.TextItem
15329  * @extends Roo.menu.BaseItem
15330  * Adds a static text string to a menu, usually used as either a heading or group separator.
15331  * Note: old style constructor with text is still supported.
15332  * 
15333  * @constructor
15334  * Creates a new TextItem
15335  * @param {Object} cfg Configuration
15336  */
15337 Roo.menu.TextItem = function(cfg){
15338     if (typeof(cfg) == 'string') {
15339         this.text = cfg;
15340     } else {
15341         Roo.apply(this,cfg);
15342     }
15343     
15344     Roo.menu.TextItem.superclass.constructor.call(this);
15345 };
15346
15347 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
15348     /**
15349      * @cfg {Boolean} text Text to show on item.
15350      */
15351     text : '',
15352     
15353     /**
15354      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15355      */
15356     hideOnClick : false,
15357     /**
15358      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
15359      */
15360     itemCls : "x-menu-text",
15361
15362     // private
15363     onRender : function(){
15364         var s = document.createElement("span");
15365         s.className = this.itemCls;
15366         s.innerHTML = this.text;
15367         this.el = s;
15368         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
15369     }
15370 });/*
15371  * Based on:
15372  * Ext JS Library 1.1.1
15373  * Copyright(c) 2006-2007, Ext JS, LLC.
15374  *
15375  * Originally Released Under LGPL - original licence link has changed is not relivant.
15376  *
15377  * Fork - LGPL
15378  * <script type="text/javascript">
15379  */
15380
15381 /**
15382  * @class Roo.menu.Separator
15383  * @extends Roo.menu.BaseItem
15384  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
15385  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
15386  * @constructor
15387  * @param {Object} config Configuration options
15388  */
15389 Roo.menu.Separator = function(config){
15390     Roo.menu.Separator.superclass.constructor.call(this, config);
15391 };
15392
15393 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
15394     /**
15395      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
15396      */
15397     itemCls : "x-menu-sep",
15398     /**
15399      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
15400      */
15401     hideOnClick : false,
15402
15403     // private
15404     onRender : function(li){
15405         var s = document.createElement("span");
15406         s.className = this.itemCls;
15407         s.innerHTML = "&#160;";
15408         this.el = s;
15409         li.addClass("x-menu-sep-li");
15410         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
15411     }
15412 });/*
15413  * Based on:
15414  * Ext JS Library 1.1.1
15415  * Copyright(c) 2006-2007, Ext JS, LLC.
15416  *
15417  * Originally Released Under LGPL - original licence link has changed is not relivant.
15418  *
15419  * Fork - LGPL
15420  * <script type="text/javascript">
15421  */
15422 /**
15423  * @class Roo.menu.Item
15424  * @extends Roo.menu.BaseItem
15425  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
15426  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
15427  * activation and click handling.
15428  * @constructor
15429  * Creates a new Item
15430  * @param {Object} config Configuration options
15431  */
15432 Roo.menu.Item = function(config){
15433     Roo.menu.Item.superclass.constructor.call(this, config);
15434     if(this.menu){
15435         this.menu = Roo.menu.MenuMgr.get(this.menu);
15436     }
15437 };
15438 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
15439     
15440     /**
15441      * @cfg {String} text
15442      * The text to show on the menu item.
15443      */
15444     text: '',
15445      /**
15446      * @cfg {String} HTML to render in menu
15447      * The text to show on the menu item (HTML version).
15448      */
15449     html: '',
15450     /**
15451      * @cfg {String} icon
15452      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
15453      */
15454     icon: undefined,
15455     /**
15456      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
15457      */
15458     itemCls : "x-menu-item",
15459     /**
15460      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
15461      */
15462     canActivate : true,
15463     /**
15464      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
15465      */
15466     showDelay: 200,
15467     // doc'd in BaseItem
15468     hideDelay: 200,
15469
15470     // private
15471     ctype: "Roo.menu.Item",
15472     
15473     // private
15474     onRender : function(container, position){
15475         var el = document.createElement("a");
15476         el.hideFocus = true;
15477         el.unselectable = "on";
15478         el.href = this.href || "#";
15479         if(this.hrefTarget){
15480             el.target = this.hrefTarget;
15481         }
15482         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
15483         
15484         var html = this.html.length ? this.html  : String.format('{0}',this.text);
15485         
15486         el.innerHTML = String.format(
15487                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
15488                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
15489         this.el = el;
15490         Roo.menu.Item.superclass.onRender.call(this, container, position);
15491     },
15492
15493     /**
15494      * Sets the text to display in this menu item
15495      * @param {String} text The text to display
15496      * @param {Boolean} isHTML true to indicate text is pure html.
15497      */
15498     setText : function(text, isHTML){
15499         if (isHTML) {
15500             this.html = text;
15501         } else {
15502             this.text = text;
15503             this.html = '';
15504         }
15505         if(this.rendered){
15506             var html = this.html.length ? this.html  : String.format('{0}',this.text);
15507      
15508             this.el.update(String.format(
15509                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
15510                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
15511             this.parentMenu.autoWidth();
15512         }
15513     },
15514
15515     // private
15516     handleClick : function(e){
15517         if(!this.href){ // if no link defined, stop the event automatically
15518             e.stopEvent();
15519         }
15520         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
15521     },
15522
15523     // private
15524     activate : function(autoExpand){
15525         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
15526             this.focus();
15527             if(autoExpand){
15528                 this.expandMenu();
15529             }
15530         }
15531         return true;
15532     },
15533
15534     // private
15535     shouldDeactivate : function(e){
15536         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
15537             if(this.menu && this.menu.isVisible()){
15538                 return !this.menu.getEl().getRegion().contains(e.getPoint());
15539             }
15540             return true;
15541         }
15542         return false;
15543     },
15544
15545     // private
15546     deactivate : function(){
15547         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
15548         this.hideMenu();
15549     },
15550
15551     // private
15552     expandMenu : function(autoActivate){
15553         if(!this.disabled && this.menu){
15554             clearTimeout(this.hideTimer);
15555             delete this.hideTimer;
15556             if(!this.menu.isVisible() && !this.showTimer){
15557                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
15558             }else if (this.menu.isVisible() && autoActivate){
15559                 this.menu.tryActivate(0, 1);
15560             }
15561         }
15562     },
15563
15564     // private
15565     deferExpand : function(autoActivate){
15566         delete this.showTimer;
15567         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
15568         if(autoActivate){
15569             this.menu.tryActivate(0, 1);
15570         }
15571     },
15572
15573     // private
15574     hideMenu : function(){
15575         clearTimeout(this.showTimer);
15576         delete this.showTimer;
15577         if(!this.hideTimer && this.menu && this.menu.isVisible()){
15578             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
15579         }
15580     },
15581
15582     // private
15583     deferHide : function(){
15584         delete this.hideTimer;
15585         this.menu.hide();
15586     }
15587 });/*
15588  * Based on:
15589  * Ext JS Library 1.1.1
15590  * Copyright(c) 2006-2007, Ext JS, LLC.
15591  *
15592  * Originally Released Under LGPL - original licence link has changed is not relivant.
15593  *
15594  * Fork - LGPL
15595  * <script type="text/javascript">
15596  */
15597  
15598 /**
15599  * @class Roo.menu.CheckItem
15600  * @extends Roo.menu.Item
15601  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
15602  * @constructor
15603  * Creates a new CheckItem
15604  * @param {Object} config Configuration options
15605  */
15606 Roo.menu.CheckItem = function(config){
15607     Roo.menu.CheckItem.superclass.constructor.call(this, config);
15608     this.addEvents({
15609         /**
15610          * @event beforecheckchange
15611          * Fires before the checked value is set, providing an opportunity to cancel if needed
15612          * @param {Roo.menu.CheckItem} this
15613          * @param {Boolean} checked The new checked value that will be set
15614          */
15615         "beforecheckchange" : true,
15616         /**
15617          * @event checkchange
15618          * Fires after the checked value has been set
15619          * @param {Roo.menu.CheckItem} this
15620          * @param {Boolean} checked The checked value that was set
15621          */
15622         "checkchange" : true
15623     });
15624     if(this.checkHandler){
15625         this.on('checkchange', this.checkHandler, this.scope);
15626     }
15627 };
15628 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
15629     /**
15630      * @cfg {String} group
15631      * All check items with the same group name will automatically be grouped into a single-select
15632      * radio button group (defaults to '')
15633      */
15634     /**
15635      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
15636      */
15637     itemCls : "x-menu-item x-menu-check-item",
15638     /**
15639      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
15640      */
15641     groupClass : "x-menu-group-item",
15642
15643     /**
15644      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
15645      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
15646      * initialized with checked = true will be rendered as checked.
15647      */
15648     checked: false,
15649
15650     // private
15651     ctype: "Roo.menu.CheckItem",
15652
15653     // private
15654     onRender : function(c){
15655         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
15656         if(this.group){
15657             this.el.addClass(this.groupClass);
15658         }
15659         Roo.menu.MenuMgr.registerCheckable(this);
15660         if(this.checked){
15661             this.checked = false;
15662             this.setChecked(true, true);
15663         }
15664     },
15665
15666     // private
15667     destroy : function(){
15668         if(this.rendered){
15669             Roo.menu.MenuMgr.unregisterCheckable(this);
15670         }
15671         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
15672     },
15673
15674     /**
15675      * Set the checked state of this item
15676      * @param {Boolean} checked The new checked value
15677      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
15678      */
15679     setChecked : function(state, suppressEvent){
15680         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
15681             if(this.container){
15682                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
15683             }
15684             this.checked = state;
15685             if(suppressEvent !== true){
15686                 this.fireEvent("checkchange", this, state);
15687             }
15688         }
15689     },
15690
15691     // private
15692     handleClick : function(e){
15693        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
15694            this.setChecked(!this.checked);
15695        }
15696        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
15697     }
15698 });/*
15699  * Based on:
15700  * Ext JS Library 1.1.1
15701  * Copyright(c) 2006-2007, Ext JS, LLC.
15702  *
15703  * Originally Released Under LGPL - original licence link has changed is not relivant.
15704  *
15705  * Fork - LGPL
15706  * <script type="text/javascript">
15707  */
15708  
15709 /**
15710  * @class Roo.menu.DateItem
15711  * @extends Roo.menu.Adapter
15712  * A menu item that wraps the {@link Roo.DatPicker} component.
15713  * @constructor
15714  * Creates a new DateItem
15715  * @param {Object} config Configuration options
15716  */
15717 Roo.menu.DateItem = function(config){
15718     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
15719     /** The Roo.DatePicker object @type Roo.DatePicker */
15720     this.picker = this.component;
15721     this.addEvents({select: true});
15722     
15723     this.picker.on("render", function(picker){
15724         picker.getEl().swallowEvent("click");
15725         picker.container.addClass("x-menu-date-item");
15726     });
15727
15728     this.picker.on("select", this.onSelect, this);
15729 };
15730
15731 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
15732     // private
15733     onSelect : function(picker, date){
15734         this.fireEvent("select", this, date, picker);
15735         Roo.menu.DateItem.superclass.handleClick.call(this);
15736     }
15737 });/*
15738  * Based on:
15739  * Ext JS Library 1.1.1
15740  * Copyright(c) 2006-2007, Ext JS, LLC.
15741  *
15742  * Originally Released Under LGPL - original licence link has changed is not relivant.
15743  *
15744  * Fork - LGPL
15745  * <script type="text/javascript">
15746  */
15747  
15748 /**
15749  * @class Roo.menu.ColorItem
15750  * @extends Roo.menu.Adapter
15751  * A menu item that wraps the {@link Roo.ColorPalette} component.
15752  * @constructor
15753  * Creates a new ColorItem
15754  * @param {Object} config Configuration options
15755  */
15756 Roo.menu.ColorItem = function(config){
15757     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
15758     /** The Roo.ColorPalette object @type Roo.ColorPalette */
15759     this.palette = this.component;
15760     this.relayEvents(this.palette, ["select"]);
15761     if(this.selectHandler){
15762         this.on('select', this.selectHandler, this.scope);
15763     }
15764 };
15765 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
15766  * Based on:
15767  * Ext JS Library 1.1.1
15768  * Copyright(c) 2006-2007, Ext JS, LLC.
15769  *
15770  * Originally Released Under LGPL - original licence link has changed is not relivant.
15771  *
15772  * Fork - LGPL
15773  * <script type="text/javascript">
15774  */
15775  
15776
15777 /**
15778  * @class Roo.menu.DateMenu
15779  * @extends Roo.menu.Menu
15780  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
15781  * @constructor
15782  * Creates a new DateMenu
15783  * @param {Object} config Configuration options
15784  */
15785 Roo.menu.DateMenu = function(config){
15786     Roo.menu.DateMenu.superclass.constructor.call(this, config);
15787     this.plain = true;
15788     var di = new Roo.menu.DateItem(config);
15789     this.add(di);
15790     /**
15791      * The {@link Roo.DatePicker} instance for this DateMenu
15792      * @type DatePicker
15793      */
15794     this.picker = di.picker;
15795     /**
15796      * @event select
15797      * @param {DatePicker} picker
15798      * @param {Date} date
15799      */
15800     this.relayEvents(di, ["select"]);
15801     this.on('beforeshow', function(){
15802         if(this.picker){
15803             this.picker.hideMonthPicker(false);
15804         }
15805     }, this);
15806 };
15807 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
15808     cls:'x-date-menu'
15809 });/*
15810  * Based on:
15811  * Ext JS Library 1.1.1
15812  * Copyright(c) 2006-2007, Ext JS, LLC.
15813  *
15814  * Originally Released Under LGPL - original licence link has changed is not relivant.
15815  *
15816  * Fork - LGPL
15817  * <script type="text/javascript">
15818  */
15819  
15820
15821 /**
15822  * @class Roo.menu.ColorMenu
15823  * @extends Roo.menu.Menu
15824  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
15825  * @constructor
15826  * Creates a new ColorMenu
15827  * @param {Object} config Configuration options
15828  */
15829 Roo.menu.ColorMenu = function(config){
15830     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
15831     this.plain = true;
15832     var ci = new Roo.menu.ColorItem(config);
15833     this.add(ci);
15834     /**
15835      * The {@link Roo.ColorPalette} instance for this ColorMenu
15836      * @type ColorPalette
15837      */
15838     this.palette = ci.palette;
15839     /**
15840      * @event select
15841      * @param {ColorPalette} palette
15842      * @param {String} color
15843      */
15844     this.relayEvents(ci, ["select"]);
15845 };
15846 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
15847  * Based on:
15848  * Ext JS Library 1.1.1
15849  * Copyright(c) 2006-2007, Ext JS, LLC.
15850  *
15851  * Originally Released Under LGPL - original licence link has changed is not relivant.
15852  *
15853  * Fork - LGPL
15854  * <script type="text/javascript">
15855  */
15856  
15857 /**
15858  * @class Roo.form.TextItem
15859  * @extends Roo.BoxComponent
15860  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15861  * @constructor
15862  * Creates a new TextItem
15863  * @param {Object} config Configuration options
15864  */
15865 Roo.form.TextItem = function(config){
15866     Roo.form.TextItem.superclass.constructor.call(this, config);
15867 };
15868
15869 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
15870     
15871     /**
15872      * @cfg {String} tag the tag for this item (default div)
15873      */
15874     tag : 'div',
15875     /**
15876      * @cfg {String} html the content for this item
15877      */
15878     html : '',
15879     
15880     getAutoCreate : function()
15881     {
15882         var cfg = {
15883             id: this.id,
15884             tag: this.tag,
15885             html: this.html,
15886             cls: 'x-form-item'
15887         };
15888         
15889         return cfg;
15890         
15891     },
15892     
15893     onRender : function(ct, position)
15894     {
15895         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
15896         
15897         if(!this.el){
15898             var cfg = this.getAutoCreate();
15899             if(!cfg.name){
15900                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
15901             }
15902             if (!cfg.name.length) {
15903                 delete cfg.name;
15904             }
15905             this.el = ct.createChild(cfg, position);
15906         }
15907     }
15908     
15909 });/*
15910  * Based on:
15911  * Ext JS Library 1.1.1
15912  * Copyright(c) 2006-2007, Ext JS, LLC.
15913  *
15914  * Originally Released Under LGPL - original licence link has changed is not relivant.
15915  *
15916  * Fork - LGPL
15917  * <script type="text/javascript">
15918  */
15919  
15920 /**
15921  * @class Roo.form.Field
15922  * @extends Roo.BoxComponent
15923  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15924  * @constructor
15925  * Creates a new Field
15926  * @param {Object} config Configuration options
15927  */
15928 Roo.form.Field = function(config){
15929     Roo.form.Field.superclass.constructor.call(this, config);
15930 };
15931
15932 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15933     /**
15934      * @cfg {String} fieldLabel Label to use when rendering a form.
15935      */
15936        /**
15937      * @cfg {String} qtip Mouse over tip
15938      */
15939      
15940     /**
15941      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15942      */
15943     invalidClass : "x-form-invalid",
15944     /**
15945      * @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")
15946      */
15947     invalidText : "The value in this field is invalid",
15948     /**
15949      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15950      */
15951     focusClass : "x-form-focus",
15952     /**
15953      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
15954       automatic validation (defaults to "keyup").
15955      */
15956     validationEvent : "keyup",
15957     /**
15958      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
15959      */
15960     validateOnBlur : true,
15961     /**
15962      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
15963      */
15964     validationDelay : 250,
15965     /**
15966      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
15967      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
15968      */
15969     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
15970     /**
15971      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
15972      */
15973     fieldClass : "x-form-field",
15974     /**
15975      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
15976      *<pre>
15977 Value         Description
15978 -----------   ----------------------------------------------------------------------
15979 qtip          Display a quick tip when the user hovers over the field
15980 title         Display a default browser title attribute popup
15981 under         Add a block div beneath the field containing the error text
15982 side          Add an error icon to the right of the field with a popup on hover
15983 [element id]  Add the error text directly to the innerHTML of the specified element
15984 </pre>
15985      */
15986     msgTarget : 'qtip',
15987     /**
15988      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
15989      */
15990     msgFx : 'normal',
15991
15992     /**
15993      * @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.
15994      */
15995     readOnly : false,
15996
15997     /**
15998      * @cfg {Boolean} disabled True to disable the field (defaults to false).
15999      */
16000     disabled : false,
16001
16002     /**
16003      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16004      */
16005     inputType : undefined,
16006     
16007     /**
16008      * @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).
16009          */
16010         tabIndex : undefined,
16011         
16012     // private
16013     isFormField : true,
16014
16015     // private
16016     hasFocus : false,
16017     /**
16018      * @property {Roo.Element} fieldEl
16019      * Element Containing the rendered Field (with label etc.)
16020      */
16021     /**
16022      * @cfg {Mixed} value A value to initialize this field with.
16023      */
16024     value : undefined,
16025
16026     /**
16027      * @cfg {String} name The field's HTML name attribute.
16028      */
16029     /**
16030      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16031      */
16032     // private
16033     loadedValue : false,
16034      
16035      
16036         // private ??
16037         initComponent : function(){
16038         Roo.form.Field.superclass.initComponent.call(this);
16039         this.addEvents({
16040             /**
16041              * @event focus
16042              * Fires when this field receives input focus.
16043              * @param {Roo.form.Field} this
16044              */
16045             focus : true,
16046             /**
16047              * @event blur
16048              * Fires when this field loses input focus.
16049              * @param {Roo.form.Field} this
16050              */
16051             blur : true,
16052             /**
16053              * @event specialkey
16054              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16055              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16056              * @param {Roo.form.Field} this
16057              * @param {Roo.EventObject} e The event object
16058              */
16059             specialkey : true,
16060             /**
16061              * @event change
16062              * Fires just before the field blurs if the field value has changed.
16063              * @param {Roo.form.Field} this
16064              * @param {Mixed} newValue The new value
16065              * @param {Mixed} oldValue The original value
16066              */
16067             change : true,
16068             /**
16069              * @event invalid
16070              * Fires after the field has been marked as invalid.
16071              * @param {Roo.form.Field} this
16072              * @param {String} msg The validation message
16073              */
16074             invalid : true,
16075             /**
16076              * @event valid
16077              * Fires after the field has been validated with no errors.
16078              * @param {Roo.form.Field} this
16079              */
16080             valid : true,
16081              /**
16082              * @event keyup
16083              * Fires after the key up
16084              * @param {Roo.form.Field} this
16085              * @param {Roo.EventObject}  e The event Object
16086              */
16087             keyup : true
16088         });
16089     },
16090
16091     /**
16092      * Returns the name attribute of the field if available
16093      * @return {String} name The field name
16094      */
16095     getName: function(){
16096          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16097     },
16098
16099     // private
16100     onRender : function(ct, position){
16101         Roo.form.Field.superclass.onRender.call(this, ct, position);
16102         if(!this.el){
16103             var cfg = this.getAutoCreate();
16104             if(!cfg.name){
16105                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16106             }
16107             if (!cfg.name.length) {
16108                 delete cfg.name;
16109             }
16110             if(this.inputType){
16111                 cfg.type = this.inputType;
16112             }
16113             this.el = ct.createChild(cfg, position);
16114         }
16115         var type = this.el.dom.type;
16116         if(type){
16117             if(type == 'password'){
16118                 type = 'text';
16119             }
16120             this.el.addClass('x-form-'+type);
16121         }
16122         if(this.readOnly){
16123             this.el.dom.readOnly = true;
16124         }
16125         if(this.tabIndex !== undefined){
16126             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16127         }
16128
16129         this.el.addClass([this.fieldClass, this.cls]);
16130         this.initValue();
16131     },
16132
16133     /**
16134      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16135      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16136      * @return {Roo.form.Field} this
16137      */
16138     applyTo : function(target){
16139         this.allowDomMove = false;
16140         this.el = Roo.get(target);
16141         this.render(this.el.dom.parentNode);
16142         return this;
16143     },
16144
16145     // private
16146     initValue : function(){
16147         if(this.value !== undefined){
16148             this.setValue(this.value);
16149         }else if(this.el.dom.value.length > 0){
16150             this.setValue(this.el.dom.value);
16151         }
16152     },
16153
16154     /**
16155      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16156      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16157      */
16158     isDirty : function() {
16159         if(this.disabled) {
16160             return false;
16161         }
16162         return String(this.getValue()) !== String(this.originalValue);
16163     },
16164
16165     /**
16166      * stores the current value in loadedValue
16167      */
16168     resetHasChanged : function()
16169     {
16170         this.loadedValue = String(this.getValue());
16171     },
16172     /**
16173      * checks the current value against the 'loaded' value.
16174      * Note - will return false if 'resetHasChanged' has not been called first.
16175      */
16176     hasChanged : function()
16177     {
16178         if(this.disabled || this.readOnly) {
16179             return false;
16180         }
16181         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16182     },
16183     
16184     
16185     
16186     // private
16187     afterRender : function(){
16188         Roo.form.Field.superclass.afterRender.call(this);
16189         this.initEvents();
16190     },
16191
16192     // private
16193     fireKey : function(e){
16194         //Roo.log('field ' + e.getKey());
16195         if(e.isNavKeyPress()){
16196             this.fireEvent("specialkey", this, e);
16197         }
16198     },
16199
16200     /**
16201      * Resets the current field value to the originally loaded value and clears any validation messages
16202      */
16203     reset : function(){
16204         this.setValue(this.resetValue);
16205         this.originalValue = this.getValue();
16206         this.clearInvalid();
16207     },
16208
16209     // private
16210     initEvents : function(){
16211         // safari killled keypress - so keydown is now used..
16212         this.el.on("keydown" , this.fireKey,  this);
16213         this.el.on("focus", this.onFocus,  this);
16214         this.el.on("blur", this.onBlur,  this);
16215         this.el.relayEvent('keyup', this);
16216
16217         // reference to original value for reset
16218         this.originalValue = this.getValue();
16219         this.resetValue =  this.getValue();
16220     },
16221
16222     // private
16223     onFocus : function(){
16224         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16225             this.el.addClass(this.focusClass);
16226         }
16227         if(!this.hasFocus){
16228             this.hasFocus = true;
16229             this.startValue = this.getValue();
16230             this.fireEvent("focus", this);
16231         }
16232     },
16233
16234     beforeBlur : Roo.emptyFn,
16235
16236     // private
16237     onBlur : function(){
16238         this.beforeBlur();
16239         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16240             this.el.removeClass(this.focusClass);
16241         }
16242         this.hasFocus = false;
16243         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16244             this.validate();
16245         }
16246         var v = this.getValue();
16247         if(String(v) !== String(this.startValue)){
16248             this.fireEvent('change', this, v, this.startValue);
16249         }
16250         this.fireEvent("blur", this);
16251     },
16252
16253     /**
16254      * Returns whether or not the field value is currently valid
16255      * @param {Boolean} preventMark True to disable marking the field invalid
16256      * @return {Boolean} True if the value is valid, else false
16257      */
16258     isValid : function(preventMark){
16259         if(this.disabled){
16260             return true;
16261         }
16262         var restore = this.preventMark;
16263         this.preventMark = preventMark === true;
16264         var v = this.validateValue(this.processValue(this.getRawValue()));
16265         this.preventMark = restore;
16266         return v;
16267     },
16268
16269     /**
16270      * Validates the field value
16271      * @return {Boolean} True if the value is valid, else false
16272      */
16273     validate : function(){
16274         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16275             this.clearInvalid();
16276             return true;
16277         }
16278         return false;
16279     },
16280
16281     processValue : function(value){
16282         return value;
16283     },
16284
16285     // private
16286     // Subclasses should provide the validation implementation by overriding this
16287     validateValue : function(value){
16288         return true;
16289     },
16290
16291     /**
16292      * Mark this field as invalid
16293      * @param {String} msg The validation message
16294      */
16295     markInvalid : function(msg){
16296         if(!this.rendered || this.preventMark){ // not rendered
16297             return;
16298         }
16299         
16300         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16301         
16302         obj.el.addClass(this.invalidClass);
16303         msg = msg || this.invalidText;
16304         switch(this.msgTarget){
16305             case 'qtip':
16306                 obj.el.dom.qtip = msg;
16307                 obj.el.dom.qclass = 'x-form-invalid-tip';
16308                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16309                     Roo.QuickTips.enable();
16310                 }
16311                 break;
16312             case 'title':
16313                 this.el.dom.title = msg;
16314                 break;
16315             case 'under':
16316                 if(!this.errorEl){
16317                     var elp = this.el.findParent('.x-form-element', 5, true);
16318                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16319                     this.errorEl.setWidth(elp.getWidth(true)-20);
16320                 }
16321                 this.errorEl.update(msg);
16322                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16323                 break;
16324             case 'side':
16325                 if(!this.errorIcon){
16326                     var elp = this.el.findParent('.x-form-element', 5, true);
16327                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16328                 }
16329                 this.alignErrorIcon();
16330                 this.errorIcon.dom.qtip = msg;
16331                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16332                 this.errorIcon.show();
16333                 this.on('resize', this.alignErrorIcon, this);
16334                 break;
16335             default:
16336                 var t = Roo.getDom(this.msgTarget);
16337                 t.innerHTML = msg;
16338                 t.style.display = this.msgDisplay;
16339                 break;
16340         }
16341         this.fireEvent('invalid', this, msg);
16342     },
16343
16344     // private
16345     alignErrorIcon : function(){
16346         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16347     },
16348
16349     /**
16350      * Clear any invalid styles/messages for this field
16351      */
16352     clearInvalid : function(){
16353         if(!this.rendered || this.preventMark){ // not rendered
16354             return;
16355         }
16356         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16357         
16358         obj.el.removeClass(this.invalidClass);
16359         switch(this.msgTarget){
16360             case 'qtip':
16361                 obj.el.dom.qtip = '';
16362                 break;
16363             case 'title':
16364                 this.el.dom.title = '';
16365                 break;
16366             case 'under':
16367                 if(this.errorEl){
16368                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16369                 }
16370                 break;
16371             case 'side':
16372                 if(this.errorIcon){
16373                     this.errorIcon.dom.qtip = '';
16374                     this.errorIcon.hide();
16375                     this.un('resize', this.alignErrorIcon, this);
16376                 }
16377                 break;
16378             default:
16379                 var t = Roo.getDom(this.msgTarget);
16380                 t.innerHTML = '';
16381                 t.style.display = 'none';
16382                 break;
16383         }
16384         this.fireEvent('valid', this);
16385     },
16386
16387     /**
16388      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16389      * @return {Mixed} value The field value
16390      */
16391     getRawValue : function(){
16392         var v = this.el.getValue();
16393         
16394         return v;
16395     },
16396
16397     /**
16398      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16399      * @return {Mixed} value The field value
16400      */
16401     getValue : function(){
16402         var v = this.el.getValue();
16403          
16404         return v;
16405     },
16406
16407     /**
16408      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16409      * @param {Mixed} value The value to set
16410      */
16411     setRawValue : function(v){
16412         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16413     },
16414
16415     /**
16416      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16417      * @param {Mixed} value The value to set
16418      */
16419     setValue : function(v){
16420         this.value = v;
16421         if(this.rendered){
16422             this.el.dom.value = (v === null || v === undefined ? '' : v);
16423              this.validate();
16424         }
16425     },
16426
16427     adjustSize : function(w, h){
16428         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16429         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16430         return s;
16431     },
16432
16433     adjustWidth : function(tag, w){
16434         tag = tag.toLowerCase();
16435         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16436             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16437                 if(tag == 'input'){
16438                     return w + 2;
16439                 }
16440                 if(tag == 'textarea'){
16441                     return w-2;
16442                 }
16443             }else if(Roo.isOpera){
16444                 if(tag == 'input'){
16445                     return w + 2;
16446                 }
16447                 if(tag == 'textarea'){
16448                     return w-2;
16449                 }
16450             }
16451         }
16452         return w;
16453     }
16454 });
16455
16456
16457 // anything other than normal should be considered experimental
16458 Roo.form.Field.msgFx = {
16459     normal : {
16460         show: function(msgEl, f){
16461             msgEl.setDisplayed('block');
16462         },
16463
16464         hide : function(msgEl, f){
16465             msgEl.setDisplayed(false).update('');
16466         }
16467     },
16468
16469     slide : {
16470         show: function(msgEl, f){
16471             msgEl.slideIn('t', {stopFx:true});
16472         },
16473
16474         hide : function(msgEl, f){
16475             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16476         }
16477     },
16478
16479     slideRight : {
16480         show: function(msgEl, f){
16481             msgEl.fixDisplay();
16482             msgEl.alignTo(f.el, 'tl-tr');
16483             msgEl.slideIn('l', {stopFx:true});
16484         },
16485
16486         hide : function(msgEl, f){
16487             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16488         }
16489     }
16490 };/*
16491  * Based on:
16492  * Ext JS Library 1.1.1
16493  * Copyright(c) 2006-2007, Ext JS, LLC.
16494  *
16495  * Originally Released Under LGPL - original licence link has changed is not relivant.
16496  *
16497  * Fork - LGPL
16498  * <script type="text/javascript">
16499  */
16500  
16501
16502 /**
16503  * @class Roo.form.TextField
16504  * @extends Roo.form.Field
16505  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16506  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16507  * @constructor
16508  * Creates a new TextField
16509  * @param {Object} config Configuration options
16510  */
16511 Roo.form.TextField = function(config){
16512     Roo.form.TextField.superclass.constructor.call(this, config);
16513     this.addEvents({
16514         /**
16515          * @event autosize
16516          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16517          * according to the default logic, but this event provides a hook for the developer to apply additional
16518          * logic at runtime to resize the field if needed.
16519              * @param {Roo.form.Field} this This text field
16520              * @param {Number} width The new field width
16521              */
16522         autosize : true
16523     });
16524 };
16525
16526 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16527     /**
16528      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16529      */
16530     grow : false,
16531     /**
16532      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16533      */
16534     growMin : 30,
16535     /**
16536      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16537      */
16538     growMax : 800,
16539     /**
16540      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16541      */
16542     vtype : null,
16543     /**
16544      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16545      */
16546     maskRe : null,
16547     /**
16548      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16549      */
16550     disableKeyFilter : false,
16551     /**
16552      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16553      */
16554     allowBlank : true,
16555     /**
16556      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16557      */
16558     minLength : 0,
16559     /**
16560      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16561      */
16562     maxLength : Number.MAX_VALUE,
16563     /**
16564      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16565      */
16566     minLengthText : "The minimum length for this field is {0}",
16567     /**
16568      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16569      */
16570     maxLengthText : "The maximum length for this field is {0}",
16571     /**
16572      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16573      */
16574     selectOnFocus : false,
16575     /**
16576      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16577      */    
16578     allowLeadingSpace : false,
16579     /**
16580      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16581      */
16582     blankText : "This field is required",
16583     /**
16584      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16585      * If available, this function will be called only after the basic validators all return true, and will be passed the
16586      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16587      */
16588     validator : null,
16589     /**
16590      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16591      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16592      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16593      */
16594     regex : null,
16595     /**
16596      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16597      */
16598     regexText : "",
16599     /**
16600      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16601      */
16602     emptyText : null,
16603    
16604
16605     // private
16606     initEvents : function()
16607     {
16608         if (this.emptyText) {
16609             this.el.attr('placeholder', this.emptyText);
16610         }
16611         
16612         Roo.form.TextField.superclass.initEvents.call(this);
16613         if(this.validationEvent == 'keyup'){
16614             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16615             this.el.on('keyup', this.filterValidation, this);
16616         }
16617         else if(this.validationEvent !== false){
16618             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16619         }
16620         
16621         if(this.selectOnFocus){
16622             this.on("focus", this.preFocus, this);
16623         }
16624         if (!this.allowLeadingSpace) {
16625             this.on('blur', this.cleanLeadingSpace, this);
16626         }
16627         
16628         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16629             this.el.on("keypress", this.filterKeys, this);
16630         }
16631         if(this.grow){
16632             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16633             this.el.on("click", this.autoSize,  this);
16634         }
16635         if(this.el.is('input[type=password]') && Roo.isSafari){
16636             this.el.on('keydown', this.SafariOnKeyDown, this);
16637         }
16638     },
16639
16640     processValue : function(value){
16641         if(this.stripCharsRe){
16642             var newValue = value.replace(this.stripCharsRe, '');
16643             if(newValue !== value){
16644                 this.setRawValue(newValue);
16645                 return newValue;
16646             }
16647         }
16648         return value;
16649     },
16650
16651     filterValidation : function(e){
16652         if(!e.isNavKeyPress()){
16653             this.validationTask.delay(this.validationDelay);
16654         }
16655     },
16656
16657     // private
16658     onKeyUp : function(e){
16659         if(!e.isNavKeyPress()){
16660             this.autoSize();
16661         }
16662     },
16663     // private - clean the leading white space
16664     cleanLeadingSpace : function(e)
16665     {
16666         if ( this.inputType == 'file') {
16667             return;
16668         }
16669         
16670         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16671     },
16672     /**
16673      * Resets the current field value to the originally-loaded value and clears any validation messages.
16674      *  
16675      */
16676     reset : function(){
16677         Roo.form.TextField.superclass.reset.call(this);
16678        
16679     }, 
16680     // private
16681     preFocus : function(){
16682         
16683         if(this.selectOnFocus){
16684             this.el.dom.select();
16685         }
16686     },
16687
16688     
16689     // private
16690     filterKeys : function(e){
16691         var k = e.getKey();
16692         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16693             return;
16694         }
16695         var c = e.getCharCode(), cc = String.fromCharCode(c);
16696         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16697             return;
16698         }
16699         if(!this.maskRe.test(cc)){
16700             e.stopEvent();
16701         }
16702     },
16703
16704     setValue : function(v){
16705         
16706         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16707         
16708         this.autoSize();
16709     },
16710
16711     /**
16712      * Validates a value according to the field's validation rules and marks the field as invalid
16713      * if the validation fails
16714      * @param {Mixed} value The value to validate
16715      * @return {Boolean} True if the value is valid, else false
16716      */
16717     validateValue : function(value){
16718         if(value.length < 1)  { // if it's blank
16719              if(this.allowBlank){
16720                 this.clearInvalid();
16721                 return true;
16722              }else{
16723                 this.markInvalid(this.blankText);
16724                 return false;
16725              }
16726         }
16727         if(value.length < this.minLength){
16728             this.markInvalid(String.format(this.minLengthText, this.minLength));
16729             return false;
16730         }
16731         if(value.length > this.maxLength){
16732             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16733             return false;
16734         }
16735         if(this.vtype){
16736             var vt = Roo.form.VTypes;
16737             if(!vt[this.vtype](value, this)){
16738                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16739                 return false;
16740             }
16741         }
16742         if(typeof this.validator == "function"){
16743             var msg = this.validator(value);
16744             if(msg !== true){
16745                 this.markInvalid(msg);
16746                 return false;
16747             }
16748         }
16749         if(this.regex && !this.regex.test(value)){
16750             this.markInvalid(this.regexText);
16751             return false;
16752         }
16753         return true;
16754     },
16755
16756     /**
16757      * Selects text in this field
16758      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16759      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16760      */
16761     selectText : function(start, end){
16762         var v = this.getRawValue();
16763         if(v.length > 0){
16764             start = start === undefined ? 0 : start;
16765             end = end === undefined ? v.length : end;
16766             var d = this.el.dom;
16767             if(d.setSelectionRange){
16768                 d.setSelectionRange(start, end);
16769             }else if(d.createTextRange){
16770                 var range = d.createTextRange();
16771                 range.moveStart("character", start);
16772                 range.moveEnd("character", v.length-end);
16773                 range.select();
16774             }
16775         }
16776     },
16777
16778     /**
16779      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16780      * This only takes effect if grow = true, and fires the autosize event.
16781      */
16782     autoSize : function(){
16783         if(!this.grow || !this.rendered){
16784             return;
16785         }
16786         if(!this.metrics){
16787             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16788         }
16789         var el = this.el;
16790         var v = el.dom.value;
16791         var d = document.createElement('div');
16792         d.appendChild(document.createTextNode(v));
16793         v = d.innerHTML;
16794         d = null;
16795         v += "&#160;";
16796         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16797         this.el.setWidth(w);
16798         this.fireEvent("autosize", this, w);
16799     },
16800     
16801     // private
16802     SafariOnKeyDown : function(event)
16803     {
16804         // this is a workaround for a password hang bug on chrome/ webkit.
16805         
16806         var isSelectAll = false;
16807         
16808         if(this.el.dom.selectionEnd > 0){
16809             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16810         }
16811         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16812             event.preventDefault();
16813             this.setValue('');
16814             return;
16815         }
16816         
16817         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16818             
16819             event.preventDefault();
16820             // this is very hacky as keydown always get's upper case.
16821             
16822             var cc = String.fromCharCode(event.getCharCode());
16823             
16824             
16825             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16826             
16827         }
16828         
16829         
16830     }
16831 });/*
16832  * Based on:
16833  * Ext JS Library 1.1.1
16834  * Copyright(c) 2006-2007, Ext JS, LLC.
16835  *
16836  * Originally Released Under LGPL - original licence link has changed is not relivant.
16837  *
16838  * Fork - LGPL
16839  * <script type="text/javascript">
16840  */
16841  
16842 /**
16843  * @class Roo.form.Hidden
16844  * @extends Roo.form.TextField
16845  * Simple Hidden element used on forms 
16846  * 
16847  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16848  * 
16849  * @constructor
16850  * Creates a new Hidden form element.
16851  * @param {Object} config Configuration options
16852  */
16853
16854
16855
16856 // easy hidden field...
16857 Roo.form.Hidden = function(config){
16858     Roo.form.Hidden.superclass.constructor.call(this, config);
16859 };
16860   
16861 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16862     fieldLabel:      '',
16863     inputType:      'hidden',
16864     width:          50,
16865     allowBlank:     true,
16866     labelSeparator: '',
16867     hidden:         true,
16868     itemCls :       'x-form-item-display-none'
16869
16870
16871 });
16872
16873
16874 /*
16875  * Based on:
16876  * Ext JS Library 1.1.1
16877  * Copyright(c) 2006-2007, Ext JS, LLC.
16878  *
16879  * Originally Released Under LGPL - original licence link has changed is not relivant.
16880  *
16881  * Fork - LGPL
16882  * <script type="text/javascript">
16883  */
16884  
16885 /**
16886  * @class Roo.form.TriggerField
16887  * @extends Roo.form.TextField
16888  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16889  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16890  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16891  * for which you can provide a custom implementation.  For example:
16892  * <pre><code>
16893 var trigger = new Roo.form.TriggerField();
16894 trigger.onTriggerClick = myTriggerFn;
16895 trigger.applyTo('my-field');
16896 </code></pre>
16897  *
16898  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16899  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16900  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16901  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16902  * @constructor
16903  * Create a new TriggerField.
16904  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16905  * to the base TextField)
16906  */
16907 Roo.form.TriggerField = function(config){
16908     this.mimicing = false;
16909     Roo.form.TriggerField.superclass.constructor.call(this, config);
16910 };
16911
16912 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16913     /**
16914      * @cfg {String} triggerClass A CSS class to apply to the trigger
16915      */
16916     /**
16917      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16918      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16919      */
16920     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16921     /**
16922      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16923      */
16924     hideTrigger:false,
16925
16926     /** @cfg {Boolean} grow @hide */
16927     /** @cfg {Number} growMin @hide */
16928     /** @cfg {Number} growMax @hide */
16929
16930     /**
16931      * @hide 
16932      * @method
16933      */
16934     autoSize: Roo.emptyFn,
16935     // private
16936     monitorTab : true,
16937     // private
16938     deferHeight : true,
16939
16940     
16941     actionMode : 'wrap',
16942     // private
16943     onResize : function(w, h){
16944         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16945         if(typeof w == 'number'){
16946             var x = w - this.trigger.getWidth();
16947             this.el.setWidth(this.adjustWidth('input', x));
16948             this.trigger.setStyle('left', x+'px');
16949         }
16950     },
16951
16952     // private
16953     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16954
16955     // private
16956     getResizeEl : function(){
16957         return this.wrap;
16958     },
16959
16960     // private
16961     getPositionEl : function(){
16962         return this.wrap;
16963     },
16964
16965     // private
16966     alignErrorIcon : function(){
16967         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
16968     },
16969
16970     // private
16971     onRender : function(ct, position){
16972         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
16973         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
16974         this.trigger = this.wrap.createChild(this.triggerConfig ||
16975                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
16976         if(this.hideTrigger){
16977             this.trigger.setDisplayed(false);
16978         }
16979         this.initTrigger();
16980         if(!this.width){
16981             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
16982         }
16983     },
16984
16985     // private
16986     initTrigger : function(){
16987         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
16988         this.trigger.addClassOnOver('x-form-trigger-over');
16989         this.trigger.addClassOnClick('x-form-trigger-click');
16990     },
16991
16992     // private
16993     onDestroy : function(){
16994         if(this.trigger){
16995             this.trigger.removeAllListeners();
16996             this.trigger.remove();
16997         }
16998         if(this.wrap){
16999             this.wrap.remove();
17000         }
17001         Roo.form.TriggerField.superclass.onDestroy.call(this);
17002     },
17003
17004     // private
17005     onFocus : function(){
17006         Roo.form.TriggerField.superclass.onFocus.call(this);
17007         if(!this.mimicing){
17008             this.wrap.addClass('x-trigger-wrap-focus');
17009             this.mimicing = true;
17010             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17011             if(this.monitorTab){
17012                 this.el.on("keydown", this.checkTab, this);
17013             }
17014         }
17015     },
17016
17017     // private
17018     checkTab : function(e){
17019         if(e.getKey() == e.TAB){
17020             this.triggerBlur();
17021         }
17022     },
17023
17024     // private
17025     onBlur : function(){
17026         // do nothing
17027     },
17028
17029     // private
17030     mimicBlur : function(e, t){
17031         if(!this.wrap.contains(t) && this.validateBlur()){
17032             this.triggerBlur();
17033         }
17034     },
17035
17036     // private
17037     triggerBlur : function(){
17038         this.mimicing = false;
17039         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17040         if(this.monitorTab){
17041             this.el.un("keydown", this.checkTab, this);
17042         }
17043         this.wrap.removeClass('x-trigger-wrap-focus');
17044         Roo.form.TriggerField.superclass.onBlur.call(this);
17045     },
17046
17047     // private
17048     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17049     validateBlur : function(e, t){
17050         return true;
17051     },
17052
17053     // private
17054     onDisable : function(){
17055         Roo.form.TriggerField.superclass.onDisable.call(this);
17056         if(this.wrap){
17057             this.wrap.addClass('x-item-disabled');
17058         }
17059     },
17060
17061     // private
17062     onEnable : function(){
17063         Roo.form.TriggerField.superclass.onEnable.call(this);
17064         if(this.wrap){
17065             this.wrap.removeClass('x-item-disabled');
17066         }
17067     },
17068
17069     // private
17070     onShow : function(){
17071         var ae = this.getActionEl();
17072         
17073         if(ae){
17074             ae.dom.style.display = '';
17075             ae.dom.style.visibility = 'visible';
17076         }
17077     },
17078
17079     // private
17080     
17081     onHide : function(){
17082         var ae = this.getActionEl();
17083         ae.dom.style.display = 'none';
17084     },
17085
17086     /**
17087      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17088      * by an implementing function.
17089      * @method
17090      * @param {EventObject} e
17091      */
17092     onTriggerClick : Roo.emptyFn
17093 });
17094
17095 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17096 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17097 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17098 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17099     initComponent : function(){
17100         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17101
17102         this.triggerConfig = {
17103             tag:'span', cls:'x-form-twin-triggers', cn:[
17104             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17105             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17106         ]};
17107     },
17108
17109     getTrigger : function(index){
17110         return this.triggers[index];
17111     },
17112
17113     initTrigger : function(){
17114         var ts = this.trigger.select('.x-form-trigger', true);
17115         this.wrap.setStyle('overflow', 'hidden');
17116         var triggerField = this;
17117         ts.each(function(t, all, index){
17118             t.hide = function(){
17119                 var w = triggerField.wrap.getWidth();
17120                 this.dom.style.display = 'none';
17121                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17122             };
17123             t.show = function(){
17124                 var w = triggerField.wrap.getWidth();
17125                 this.dom.style.display = '';
17126                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17127             };
17128             var triggerIndex = 'Trigger'+(index+1);
17129
17130             if(this['hide'+triggerIndex]){
17131                 t.dom.style.display = 'none';
17132             }
17133             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17134             t.addClassOnOver('x-form-trigger-over');
17135             t.addClassOnClick('x-form-trigger-click');
17136         }, this);
17137         this.triggers = ts.elements;
17138     },
17139
17140     onTrigger1Click : Roo.emptyFn,
17141     onTrigger2Click : Roo.emptyFn
17142 });/*
17143  * Based on:
17144  * Ext JS Library 1.1.1
17145  * Copyright(c) 2006-2007, Ext JS, LLC.
17146  *
17147  * Originally Released Under LGPL - original licence link has changed is not relivant.
17148  *
17149  * Fork - LGPL
17150  * <script type="text/javascript">
17151  */
17152  
17153 /**
17154  * @class Roo.form.TextArea
17155  * @extends Roo.form.TextField
17156  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17157  * support for auto-sizing.
17158  * @constructor
17159  * Creates a new TextArea
17160  * @param {Object} config Configuration options
17161  */
17162 Roo.form.TextArea = function(config){
17163     Roo.form.TextArea.superclass.constructor.call(this, config);
17164     // these are provided exchanges for backwards compat
17165     // minHeight/maxHeight were replaced by growMin/growMax to be
17166     // compatible with TextField growing config values
17167     if(this.minHeight !== undefined){
17168         this.growMin = this.minHeight;
17169     }
17170     if(this.maxHeight !== undefined){
17171         this.growMax = this.maxHeight;
17172     }
17173 };
17174
17175 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17176     /**
17177      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17178      */
17179     growMin : 60,
17180     /**
17181      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17182      */
17183     growMax: 1000,
17184     /**
17185      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17186      * in the field (equivalent to setting overflow: hidden, defaults to false)
17187      */
17188     preventScrollbars: false,
17189     /**
17190      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17191      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17192      */
17193
17194     // private
17195     onRender : function(ct, position){
17196         if(!this.el){
17197             this.defaultAutoCreate = {
17198                 tag: "textarea",
17199                 style:"width:300px;height:60px;",
17200                 autocomplete: "new-password"
17201             };
17202         }
17203         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17204         if(this.grow){
17205             this.textSizeEl = Roo.DomHelper.append(document.body, {
17206                 tag: "pre", cls: "x-form-grow-sizer"
17207             });
17208             if(this.preventScrollbars){
17209                 this.el.setStyle("overflow", "hidden");
17210             }
17211             this.el.setHeight(this.growMin);
17212         }
17213     },
17214
17215     onDestroy : function(){
17216         if(this.textSizeEl){
17217             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17218         }
17219         Roo.form.TextArea.superclass.onDestroy.call(this);
17220     },
17221
17222     // private
17223     onKeyUp : function(e){
17224         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17225             this.autoSize();
17226         }
17227     },
17228
17229     /**
17230      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17231      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17232      */
17233     autoSize : function(){
17234         if(!this.grow || !this.textSizeEl){
17235             return;
17236         }
17237         var el = this.el;
17238         var v = el.dom.value;
17239         var ts = this.textSizeEl;
17240
17241         ts.innerHTML = '';
17242         ts.appendChild(document.createTextNode(v));
17243         v = ts.innerHTML;
17244
17245         Roo.fly(ts).setWidth(this.el.getWidth());
17246         if(v.length < 1){
17247             v = "&#160;&#160;";
17248         }else{
17249             if(Roo.isIE){
17250                 v = v.replace(/\n/g, '<p>&#160;</p>');
17251             }
17252             v += "&#160;\n&#160;";
17253         }
17254         ts.innerHTML = v;
17255         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17256         if(h != this.lastHeight){
17257             this.lastHeight = h;
17258             this.el.setHeight(h);
17259             this.fireEvent("autosize", this, h);
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 /**
17275  * @class Roo.form.NumberField
17276  * @extends Roo.form.TextField
17277  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17278  * @constructor
17279  * Creates a new NumberField
17280  * @param {Object} config Configuration options
17281  */
17282 Roo.form.NumberField = function(config){
17283     Roo.form.NumberField.superclass.constructor.call(this, config);
17284 };
17285
17286 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17287     /**
17288      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17289      */
17290     fieldClass: "x-form-field x-form-num-field",
17291     /**
17292      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17293      */
17294     allowDecimals : true,
17295     /**
17296      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17297      */
17298     decimalSeparator : ".",
17299     /**
17300      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17301      */
17302     decimalPrecision : 2,
17303     /**
17304      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17305      */
17306     allowNegative : true,
17307     /**
17308      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17309      */
17310     minValue : Number.NEGATIVE_INFINITY,
17311     /**
17312      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17313      */
17314     maxValue : Number.MAX_VALUE,
17315     /**
17316      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17317      */
17318     minText : "The minimum value for this field is {0}",
17319     /**
17320      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17321      */
17322     maxText : "The maximum value for this field is {0}",
17323     /**
17324      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17325      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17326      */
17327     nanText : "{0} is not a valid number",
17328
17329     // private
17330     initEvents : function(){
17331         Roo.form.NumberField.superclass.initEvents.call(this);
17332         var allowed = "0123456789";
17333         if(this.allowDecimals){
17334             allowed += this.decimalSeparator;
17335         }
17336         if(this.allowNegative){
17337             allowed += "-";
17338         }
17339         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17340         var keyPress = function(e){
17341             var k = e.getKey();
17342             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17343                 return;
17344             }
17345             var c = e.getCharCode();
17346             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17347                 e.stopEvent();
17348             }
17349         };
17350         this.el.on("keypress", keyPress, this);
17351     },
17352
17353     // private
17354     validateValue : function(value){
17355         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17356             return false;
17357         }
17358         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17359              return true;
17360         }
17361         var num = this.parseValue(value);
17362         if(isNaN(num)){
17363             this.markInvalid(String.format(this.nanText, value));
17364             return false;
17365         }
17366         if(num < this.minValue){
17367             this.markInvalid(String.format(this.minText, this.minValue));
17368             return false;
17369         }
17370         if(num > this.maxValue){
17371             this.markInvalid(String.format(this.maxText, this.maxValue));
17372             return false;
17373         }
17374         return true;
17375     },
17376
17377     getValue : function(){
17378         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17379     },
17380
17381     // private
17382     parseValue : function(value){
17383         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17384         return isNaN(value) ? '' : value;
17385     },
17386
17387     // private
17388     fixPrecision : function(value){
17389         var nan = isNaN(value);
17390         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17391             return nan ? '' : value;
17392         }
17393         return parseFloat(value).toFixed(this.decimalPrecision);
17394     },
17395
17396     setValue : function(v){
17397         v = this.fixPrecision(v);
17398         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17399     },
17400
17401     // private
17402     decimalPrecisionFcn : function(v){
17403         return Math.floor(v);
17404     },
17405
17406     beforeBlur : function(){
17407         var v = this.parseValue(this.getRawValue());
17408         if(v){
17409             this.setValue(v);
17410         }
17411     }
17412 });/*
17413  * Based on:
17414  * Ext JS Library 1.1.1
17415  * Copyright(c) 2006-2007, Ext JS, LLC.
17416  *
17417  * Originally Released Under LGPL - original licence link has changed is not relivant.
17418  *
17419  * Fork - LGPL
17420  * <script type="text/javascript">
17421  */
17422  
17423 /**
17424  * @class Roo.form.DateField
17425  * @extends Roo.form.TriggerField
17426  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17427 * @constructor
17428 * Create a new DateField
17429 * @param {Object} config
17430  */
17431 Roo.form.DateField = function(config)
17432 {
17433     Roo.form.DateField.superclass.constructor.call(this, config);
17434     
17435       this.addEvents({
17436          
17437         /**
17438          * @event select
17439          * Fires when a date is selected
17440              * @param {Roo.form.DateField} combo This combo box
17441              * @param {Date} date The date selected
17442              */
17443         'select' : true
17444          
17445     });
17446     
17447     
17448     if(typeof this.minValue == "string") {
17449         this.minValue = this.parseDate(this.minValue);
17450     }
17451     if(typeof this.maxValue == "string") {
17452         this.maxValue = this.parseDate(this.maxValue);
17453     }
17454     this.ddMatch = null;
17455     if(this.disabledDates){
17456         var dd = this.disabledDates;
17457         var re = "(?:";
17458         for(var i = 0; i < dd.length; i++){
17459             re += dd[i];
17460             if(i != dd.length-1) {
17461                 re += "|";
17462             }
17463         }
17464         this.ddMatch = new RegExp(re + ")");
17465     }
17466 };
17467
17468 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17469     /**
17470      * @cfg {String} format
17471      * The default date format string which can be overriden for localization support.  The format must be
17472      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17473      */
17474     format : "m/d/y",
17475     /**
17476      * @cfg {String} altFormats
17477      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17478      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17479      */
17480     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17481     /**
17482      * @cfg {Array} disabledDays
17483      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17484      */
17485     disabledDays : null,
17486     /**
17487      * @cfg {String} disabledDaysText
17488      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17489      */
17490     disabledDaysText : "Disabled",
17491     /**
17492      * @cfg {Array} disabledDates
17493      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17494      * expression so they are very powerful. Some examples:
17495      * <ul>
17496      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17497      * <li>["03/08", "09/16"] would disable those days for every year</li>
17498      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17499      * <li>["03/../2006"] would disable every day in March 2006</li>
17500      * <li>["^03"] would disable every day in every March</li>
17501      * </ul>
17502      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17503      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17504      */
17505     disabledDates : null,
17506     /**
17507      * @cfg {String} disabledDatesText
17508      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17509      */
17510     disabledDatesText : "Disabled",
17511     /**
17512      * @cfg {Date/String} minValue
17513      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17514      * valid format (defaults to null).
17515      */
17516     minValue : null,
17517     /**
17518      * @cfg {Date/String} maxValue
17519      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17520      * valid format (defaults to null).
17521      */
17522     maxValue : null,
17523     /**
17524      * @cfg {String} minText
17525      * The error text to display when the date in the cell is before minValue (defaults to
17526      * 'The date in this field must be after {minValue}').
17527      */
17528     minText : "The date in this field must be equal to or after {0}",
17529     /**
17530      * @cfg {String} maxText
17531      * The error text to display when the date in the cell is after maxValue (defaults to
17532      * 'The date in this field must be before {maxValue}').
17533      */
17534     maxText : "The date in this field must be equal to or before {0}",
17535     /**
17536      * @cfg {String} invalidText
17537      * The error text to display when the date in the field is invalid (defaults to
17538      * '{value} is not a valid date - it must be in the format {format}').
17539      */
17540     invalidText : "{0} is not a valid date - it must be in the format {1}",
17541     /**
17542      * @cfg {String} triggerClass
17543      * An additional CSS class used to style the trigger button.  The trigger will always get the
17544      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17545      * which displays a calendar icon).
17546      */
17547     triggerClass : 'x-form-date-trigger',
17548     
17549
17550     /**
17551      * @cfg {Boolean} useIso
17552      * if enabled, then the date field will use a hidden field to store the 
17553      * real value as iso formated date. default (false)
17554      */ 
17555     useIso : false,
17556     /**
17557      * @cfg {String/Object} autoCreate
17558      * A DomHelper element spec, or true for a default element spec (defaults to
17559      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17560      */ 
17561     // private
17562     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17563     
17564     // private
17565     hiddenField: false,
17566     
17567     onRender : function(ct, position)
17568     {
17569         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17570         if (this.useIso) {
17571             //this.el.dom.removeAttribute('name'); 
17572             Roo.log("Changing name?");
17573             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17574             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17575                     'before', true);
17576             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17577             // prevent input submission
17578             this.hiddenName = this.name;
17579         }
17580             
17581             
17582     },
17583     
17584     // private
17585     validateValue : function(value)
17586     {
17587         value = this.formatDate(value);
17588         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17589             Roo.log('super failed');
17590             return false;
17591         }
17592         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17593              return true;
17594         }
17595         var svalue = value;
17596         value = this.parseDate(value);
17597         if(!value){
17598             Roo.log('parse date failed' + svalue);
17599             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17600             return false;
17601         }
17602         var time = value.getTime();
17603         if(this.minValue && time < this.minValue.getTime()){
17604             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17605             return false;
17606         }
17607         if(this.maxValue && time > this.maxValue.getTime()){
17608             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17609             return false;
17610         }
17611         if(this.disabledDays){
17612             var day = value.getDay();
17613             for(var i = 0; i < this.disabledDays.length; i++) {
17614                 if(day === this.disabledDays[i]){
17615                     this.markInvalid(this.disabledDaysText);
17616                     return false;
17617                 }
17618             }
17619         }
17620         var fvalue = this.formatDate(value);
17621         if(this.ddMatch && this.ddMatch.test(fvalue)){
17622             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17623             return false;
17624         }
17625         return true;
17626     },
17627
17628     // private
17629     // Provides logic to override the default TriggerField.validateBlur which just returns true
17630     validateBlur : function(){
17631         return !this.menu || !this.menu.isVisible();
17632     },
17633     
17634     getName: function()
17635     {
17636         // returns hidden if it's set..
17637         if (!this.rendered) {return ''};
17638         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17639         
17640     },
17641
17642     /**
17643      * Returns the current date value of the date field.
17644      * @return {Date} The date value
17645      */
17646     getValue : function(){
17647         
17648         return  this.hiddenField ?
17649                 this.hiddenField.value :
17650                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17651     },
17652
17653     /**
17654      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17655      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17656      * (the default format used is "m/d/y").
17657      * <br />Usage:
17658      * <pre><code>
17659 //All of these calls set the same date value (May 4, 2006)
17660
17661 //Pass a date object:
17662 var dt = new Date('5/4/06');
17663 dateField.setValue(dt);
17664
17665 //Pass a date string (default format):
17666 dateField.setValue('5/4/06');
17667
17668 //Pass a date string (custom format):
17669 dateField.format = 'Y-m-d';
17670 dateField.setValue('2006-5-4');
17671 </code></pre>
17672      * @param {String/Date} date The date or valid date string
17673      */
17674     setValue : function(date){
17675         if (this.hiddenField) {
17676             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17677         }
17678         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17679         // make sure the value field is always stored as a date..
17680         this.value = this.parseDate(date);
17681         
17682         
17683     },
17684
17685     // private
17686     parseDate : function(value){
17687         if(!value || value instanceof Date){
17688             return value;
17689         }
17690         var v = Date.parseDate(value, this.format);
17691          if (!v && this.useIso) {
17692             v = Date.parseDate(value, 'Y-m-d');
17693         }
17694         if(!v && this.altFormats){
17695             if(!this.altFormatsArray){
17696                 this.altFormatsArray = this.altFormats.split("|");
17697             }
17698             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17699                 v = Date.parseDate(value, this.altFormatsArray[i]);
17700             }
17701         }
17702         return v;
17703     },
17704
17705     // private
17706     formatDate : function(date, fmt){
17707         return (!date || !(date instanceof Date)) ?
17708                date : date.dateFormat(fmt || this.format);
17709     },
17710
17711     // private
17712     menuListeners : {
17713         select: function(m, d){
17714             
17715             this.setValue(d);
17716             this.fireEvent('select', this, d);
17717         },
17718         show : function(){ // retain focus styling
17719             this.onFocus();
17720         },
17721         hide : function(){
17722             this.focus.defer(10, this);
17723             var ml = this.menuListeners;
17724             this.menu.un("select", ml.select,  this);
17725             this.menu.un("show", ml.show,  this);
17726             this.menu.un("hide", ml.hide,  this);
17727         }
17728     },
17729
17730     // private
17731     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17732     onTriggerClick : function(){
17733         if(this.disabled){
17734             return;
17735         }
17736         if(this.menu == null){
17737             this.menu = new Roo.menu.DateMenu();
17738         }
17739         Roo.apply(this.menu.picker,  {
17740             showClear: this.allowBlank,
17741             minDate : this.minValue,
17742             maxDate : this.maxValue,
17743             disabledDatesRE : this.ddMatch,
17744             disabledDatesText : this.disabledDatesText,
17745             disabledDays : this.disabledDays,
17746             disabledDaysText : this.disabledDaysText,
17747             format : this.useIso ? 'Y-m-d' : this.format,
17748             minText : String.format(this.minText, this.formatDate(this.minValue)),
17749             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17750         });
17751         this.menu.on(Roo.apply({}, this.menuListeners, {
17752             scope:this
17753         }));
17754         this.menu.picker.setValue(this.getValue() || new Date());
17755         this.menu.show(this.el, "tl-bl?");
17756     },
17757
17758     beforeBlur : function(){
17759         var v = this.parseDate(this.getRawValue());
17760         if(v){
17761             this.setValue(v);
17762         }
17763     },
17764
17765     /*@
17766      * overide
17767      * 
17768      */
17769     isDirty : function() {
17770         if(this.disabled) {
17771             return false;
17772         }
17773         
17774         if(typeof(this.startValue) === 'undefined'){
17775             return false;
17776         }
17777         
17778         return String(this.getValue()) !== String(this.startValue);
17779         
17780     },
17781     // @overide
17782     cleanLeadingSpace : function(e)
17783     {
17784        return;
17785     }
17786     
17787 });/*
17788  * Based on:
17789  * Ext JS Library 1.1.1
17790  * Copyright(c) 2006-2007, Ext JS, LLC.
17791  *
17792  * Originally Released Under LGPL - original licence link has changed is not relivant.
17793  *
17794  * Fork - LGPL
17795  * <script type="text/javascript">
17796  */
17797  
17798 /**
17799  * @class Roo.form.MonthField
17800  * @extends Roo.form.TriggerField
17801  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17802 * @constructor
17803 * Create a new MonthField
17804 * @param {Object} config
17805  */
17806 Roo.form.MonthField = function(config){
17807     
17808     Roo.form.MonthField.superclass.constructor.call(this, config);
17809     
17810       this.addEvents({
17811          
17812         /**
17813          * @event select
17814          * Fires when a date is selected
17815              * @param {Roo.form.MonthFieeld} combo This combo box
17816              * @param {Date} date The date selected
17817              */
17818         'select' : true
17819          
17820     });
17821     
17822     
17823     if(typeof this.minValue == "string") {
17824         this.minValue = this.parseDate(this.minValue);
17825     }
17826     if(typeof this.maxValue == "string") {
17827         this.maxValue = this.parseDate(this.maxValue);
17828     }
17829     this.ddMatch = null;
17830     if(this.disabledDates){
17831         var dd = this.disabledDates;
17832         var re = "(?:";
17833         for(var i = 0; i < dd.length; i++){
17834             re += dd[i];
17835             if(i != dd.length-1) {
17836                 re += "|";
17837             }
17838         }
17839         this.ddMatch = new RegExp(re + ")");
17840     }
17841 };
17842
17843 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17844     /**
17845      * @cfg {String} format
17846      * The default date format string which can be overriden for localization support.  The format must be
17847      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17848      */
17849     format : "M Y",
17850     /**
17851      * @cfg {String} altFormats
17852      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17853      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17854      */
17855     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17856     /**
17857      * @cfg {Array} disabledDays
17858      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17859      */
17860     disabledDays : [0,1,2,3,4,5,6],
17861     /**
17862      * @cfg {String} disabledDaysText
17863      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17864      */
17865     disabledDaysText : "Disabled",
17866     /**
17867      * @cfg {Array} disabledDates
17868      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17869      * expression so they are very powerful. Some examples:
17870      * <ul>
17871      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17872      * <li>["03/08", "09/16"] would disable those days for every year</li>
17873      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17874      * <li>["03/../2006"] would disable every day in March 2006</li>
17875      * <li>["^03"] would disable every day in every March</li>
17876      * </ul>
17877      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17878      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17879      */
17880     disabledDates : null,
17881     /**
17882      * @cfg {String} disabledDatesText
17883      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17884      */
17885     disabledDatesText : "Disabled",
17886     /**
17887      * @cfg {Date/String} minValue
17888      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17889      * valid format (defaults to null).
17890      */
17891     minValue : null,
17892     /**
17893      * @cfg {Date/String} maxValue
17894      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17895      * valid format (defaults to null).
17896      */
17897     maxValue : null,
17898     /**
17899      * @cfg {String} minText
17900      * The error text to display when the date in the cell is before minValue (defaults to
17901      * 'The date in this field must be after {minValue}').
17902      */
17903     minText : "The date in this field must be equal to or after {0}",
17904     /**
17905      * @cfg {String} maxTextf
17906      * The error text to display when the date in the cell is after maxValue (defaults to
17907      * 'The date in this field must be before {maxValue}').
17908      */
17909     maxText : "The date in this field must be equal to or before {0}",
17910     /**
17911      * @cfg {String} invalidText
17912      * The error text to display when the date in the field is invalid (defaults to
17913      * '{value} is not a valid date - it must be in the format {format}').
17914      */
17915     invalidText : "{0} is not a valid date - it must be in the format {1}",
17916     /**
17917      * @cfg {String} triggerClass
17918      * An additional CSS class used to style the trigger button.  The trigger will always get the
17919      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17920      * which displays a calendar icon).
17921      */
17922     triggerClass : 'x-form-date-trigger',
17923     
17924
17925     /**
17926      * @cfg {Boolean} useIso
17927      * if enabled, then the date field will use a hidden field to store the 
17928      * real value as iso formated date. default (true)
17929      */ 
17930     useIso : true,
17931     /**
17932      * @cfg {String/Object} autoCreate
17933      * A DomHelper element spec, or true for a default element spec (defaults to
17934      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17935      */ 
17936     // private
17937     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17938     
17939     // private
17940     hiddenField: false,
17941     
17942     hideMonthPicker : false,
17943     
17944     onRender : function(ct, position)
17945     {
17946         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17947         if (this.useIso) {
17948             this.el.dom.removeAttribute('name'); 
17949             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17950                     'before', true);
17951             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17952             // prevent input submission
17953             this.hiddenName = this.name;
17954         }
17955             
17956             
17957     },
17958     
17959     // private
17960     validateValue : function(value)
17961     {
17962         value = this.formatDate(value);
17963         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
17964             return false;
17965         }
17966         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17967              return true;
17968         }
17969         var svalue = value;
17970         value = this.parseDate(value);
17971         if(!value){
17972             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17973             return false;
17974         }
17975         var time = value.getTime();
17976         if(this.minValue && time < this.minValue.getTime()){
17977             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17978             return false;
17979         }
17980         if(this.maxValue && time > this.maxValue.getTime()){
17981             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17982             return false;
17983         }
17984         /*if(this.disabledDays){
17985             var day = value.getDay();
17986             for(var i = 0; i < this.disabledDays.length; i++) {
17987                 if(day === this.disabledDays[i]){
17988                     this.markInvalid(this.disabledDaysText);
17989                     return false;
17990                 }
17991             }
17992         }
17993         */
17994         var fvalue = this.formatDate(value);
17995         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
17996             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17997             return false;
17998         }
17999         */
18000         return true;
18001     },
18002
18003     // private
18004     // Provides logic to override the default TriggerField.validateBlur which just returns true
18005     validateBlur : function(){
18006         return !this.menu || !this.menu.isVisible();
18007     },
18008
18009     /**
18010      * Returns the current date value of the date field.
18011      * @return {Date} The date value
18012      */
18013     getValue : function(){
18014         
18015         
18016         
18017         return  this.hiddenField ?
18018                 this.hiddenField.value :
18019                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18020     },
18021
18022     /**
18023      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18024      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18025      * (the default format used is "m/d/y").
18026      * <br />Usage:
18027      * <pre><code>
18028 //All of these calls set the same date value (May 4, 2006)
18029
18030 //Pass a date object:
18031 var dt = new Date('5/4/06');
18032 monthField.setValue(dt);
18033
18034 //Pass a date string (default format):
18035 monthField.setValue('5/4/06');
18036
18037 //Pass a date string (custom format):
18038 monthField.format = 'Y-m-d';
18039 monthField.setValue('2006-5-4');
18040 </code></pre>
18041      * @param {String/Date} date The date or valid date string
18042      */
18043     setValue : function(date){
18044         Roo.log('month setValue' + date);
18045         // can only be first of month..
18046         
18047         var val = this.parseDate(date);
18048         
18049         if (this.hiddenField) {
18050             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18051         }
18052         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18053         this.value = this.parseDate(date);
18054     },
18055
18056     // private
18057     parseDate : function(value){
18058         if(!value || value instanceof Date){
18059             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18060             return value;
18061         }
18062         var v = Date.parseDate(value, this.format);
18063         if (!v && this.useIso) {
18064             v = Date.parseDate(value, 'Y-m-d');
18065         }
18066         if (v) {
18067             // 
18068             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18069         }
18070         
18071         
18072         if(!v && this.altFormats){
18073             if(!this.altFormatsArray){
18074                 this.altFormatsArray = this.altFormats.split("|");
18075             }
18076             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18077                 v = Date.parseDate(value, this.altFormatsArray[i]);
18078             }
18079         }
18080         return v;
18081     },
18082
18083     // private
18084     formatDate : function(date, fmt){
18085         return (!date || !(date instanceof Date)) ?
18086                date : date.dateFormat(fmt || this.format);
18087     },
18088
18089     // private
18090     menuListeners : {
18091         select: function(m, d){
18092             this.setValue(d);
18093             this.fireEvent('select', this, d);
18094         },
18095         show : function(){ // retain focus styling
18096             this.onFocus();
18097         },
18098         hide : function(){
18099             this.focus.defer(10, this);
18100             var ml = this.menuListeners;
18101             this.menu.un("select", ml.select,  this);
18102             this.menu.un("show", ml.show,  this);
18103             this.menu.un("hide", ml.hide,  this);
18104         }
18105     },
18106     // private
18107     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18108     onTriggerClick : function(){
18109         if(this.disabled){
18110             return;
18111         }
18112         if(this.menu == null){
18113             this.menu = new Roo.menu.DateMenu();
18114            
18115         }
18116         
18117         Roo.apply(this.menu.picker,  {
18118             
18119             showClear: this.allowBlank,
18120             minDate : this.minValue,
18121             maxDate : this.maxValue,
18122             disabledDatesRE : this.ddMatch,
18123             disabledDatesText : this.disabledDatesText,
18124             
18125             format : this.useIso ? 'Y-m-d' : this.format,
18126             minText : String.format(this.minText, this.formatDate(this.minValue)),
18127             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18128             
18129         });
18130          this.menu.on(Roo.apply({}, this.menuListeners, {
18131             scope:this
18132         }));
18133        
18134         
18135         var m = this.menu;
18136         var p = m.picker;
18137         
18138         // hide month picker get's called when we called by 'before hide';
18139         
18140         var ignorehide = true;
18141         p.hideMonthPicker  = function(disableAnim){
18142             if (ignorehide) {
18143                 return;
18144             }
18145              if(this.monthPicker){
18146                 Roo.log("hideMonthPicker called");
18147                 if(disableAnim === true){
18148                     this.monthPicker.hide();
18149                 }else{
18150                     this.monthPicker.slideOut('t', {duration:.2});
18151                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18152                     p.fireEvent("select", this, this.value);
18153                     m.hide();
18154                 }
18155             }
18156         }
18157         
18158         Roo.log('picker set value');
18159         Roo.log(this.getValue());
18160         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18161         m.show(this.el, 'tl-bl?');
18162         ignorehide  = false;
18163         // this will trigger hideMonthPicker..
18164         
18165         
18166         // hidden the day picker
18167         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18168         
18169         
18170         
18171       
18172         
18173         p.showMonthPicker.defer(100, p);
18174     
18175         
18176        
18177     },
18178
18179     beforeBlur : function(){
18180         var v = this.parseDate(this.getRawValue());
18181         if(v){
18182             this.setValue(v);
18183         }
18184     }
18185
18186     /** @cfg {Boolean} grow @hide */
18187     /** @cfg {Number} growMin @hide */
18188     /** @cfg {Number} growMax @hide */
18189     /**
18190      * @hide
18191      * @method autoSize
18192      */
18193 });/*
18194  * Based on:
18195  * Ext JS Library 1.1.1
18196  * Copyright(c) 2006-2007, Ext JS, LLC.
18197  *
18198  * Originally Released Under LGPL - original licence link has changed is not relivant.
18199  *
18200  * Fork - LGPL
18201  * <script type="text/javascript">
18202  */
18203  
18204
18205 /**
18206  * @class Roo.form.ComboBox
18207  * @extends Roo.form.TriggerField
18208  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18209  * @constructor
18210  * Create a new ComboBox.
18211  * @param {Object} config Configuration options
18212  */
18213 Roo.form.ComboBox = function(config){
18214     Roo.form.ComboBox.superclass.constructor.call(this, config);
18215     this.addEvents({
18216         /**
18217          * @event expand
18218          * Fires when the dropdown list is expanded
18219              * @param {Roo.form.ComboBox} combo This combo box
18220              */
18221         'expand' : true,
18222         /**
18223          * @event collapse
18224          * Fires when the dropdown list is collapsed
18225              * @param {Roo.form.ComboBox} combo This combo box
18226              */
18227         'collapse' : true,
18228         /**
18229          * @event beforeselect
18230          * Fires before a list item is selected. Return false to cancel the selection.
18231              * @param {Roo.form.ComboBox} combo This combo box
18232              * @param {Roo.data.Record} record The data record returned from the underlying store
18233              * @param {Number} index The index of the selected item in the dropdown list
18234              */
18235         'beforeselect' : true,
18236         /**
18237          * @event select
18238          * Fires when a list item is selected
18239              * @param {Roo.form.ComboBox} combo This combo box
18240              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18241              * @param {Number} index The index of the selected item in the dropdown list
18242              */
18243         'select' : true,
18244         /**
18245          * @event beforequery
18246          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18247          * The event object passed has these properties:
18248              * @param {Roo.form.ComboBox} combo This combo box
18249              * @param {String} query The query
18250              * @param {Boolean} forceAll true to force "all" query
18251              * @param {Boolean} cancel true to cancel the query
18252              * @param {Object} e The query event object
18253              */
18254         'beforequery': true,
18255          /**
18256          * @event add
18257          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18258              * @param {Roo.form.ComboBox} combo This combo box
18259              */
18260         'add' : true,
18261         /**
18262          * @event edit
18263          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18264              * @param {Roo.form.ComboBox} combo This combo box
18265              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18266              */
18267         'edit' : true
18268         
18269         
18270     });
18271     if(this.transform){
18272         this.allowDomMove = false;
18273         var s = Roo.getDom(this.transform);
18274         if(!this.hiddenName){
18275             this.hiddenName = s.name;
18276         }
18277         if(!this.store){
18278             this.mode = 'local';
18279             var d = [], opts = s.options;
18280             for(var i = 0, len = opts.length;i < len; i++){
18281                 var o = opts[i];
18282                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18283                 if(o.selected) {
18284                     this.value = value;
18285                 }
18286                 d.push([value, o.text]);
18287             }
18288             this.store = new Roo.data.SimpleStore({
18289                 'id': 0,
18290                 fields: ['value', 'text'],
18291                 data : d
18292             });
18293             this.valueField = 'value';
18294             this.displayField = 'text';
18295         }
18296         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18297         if(!this.lazyRender){
18298             this.target = true;
18299             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18300             s.parentNode.removeChild(s); // remove it
18301             this.render(this.el.parentNode);
18302         }else{
18303             s.parentNode.removeChild(s); // remove it
18304         }
18305
18306     }
18307     if (this.store) {
18308         this.store = Roo.factory(this.store, Roo.data);
18309     }
18310     
18311     this.selectedIndex = -1;
18312     if(this.mode == 'local'){
18313         if(config.queryDelay === undefined){
18314             this.queryDelay = 10;
18315         }
18316         if(config.minChars === undefined){
18317             this.minChars = 0;
18318         }
18319     }
18320 };
18321
18322 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18323     /**
18324      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18325      */
18326     /**
18327      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18328      * rendering into an Roo.Editor, defaults to false)
18329      */
18330     /**
18331      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18332      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18333      */
18334     /**
18335      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18336      */
18337     /**
18338      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18339      * the dropdown list (defaults to undefined, with no header element)
18340      */
18341
18342      /**
18343      * @cfg {String/Roo.Template} tpl The template to use to render the output
18344      */
18345      
18346     // private
18347     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18348     /**
18349      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18350      */
18351     listWidth: undefined,
18352     /**
18353      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18354      * mode = 'remote' or 'text' if mode = 'local')
18355      */
18356     displayField: undefined,
18357     /**
18358      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18359      * mode = 'remote' or 'value' if mode = 'local'). 
18360      * Note: use of a valueField requires the user make a selection
18361      * in order for a value to be mapped.
18362      */
18363     valueField: undefined,
18364     
18365     
18366     /**
18367      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18368      * field's data value (defaults to the underlying DOM element's name)
18369      */
18370     hiddenName: undefined,
18371     /**
18372      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18373      */
18374     listClass: '',
18375     /**
18376      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18377      */
18378     selectedClass: 'x-combo-selected',
18379     /**
18380      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18381      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18382      * which displays a downward arrow icon).
18383      */
18384     triggerClass : 'x-form-arrow-trigger',
18385     /**
18386      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18387      */
18388     shadow:'sides',
18389     /**
18390      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18391      * anchor positions (defaults to 'tl-bl')
18392      */
18393     listAlign: 'tl-bl?',
18394     /**
18395      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18396      */
18397     maxHeight: 300,
18398     /**
18399      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18400      * query specified by the allQuery config option (defaults to 'query')
18401      */
18402     triggerAction: 'query',
18403     /**
18404      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18405      * (defaults to 4, does not apply if editable = false)
18406      */
18407     minChars : 4,
18408     /**
18409      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18410      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18411      */
18412     typeAhead: false,
18413     /**
18414      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18415      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18416      */
18417     queryDelay: 500,
18418     /**
18419      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18420      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18421      */
18422     pageSize: 0,
18423     /**
18424      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18425      * when editable = true (defaults to false)
18426      */
18427     selectOnFocus:false,
18428     /**
18429      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18430      */
18431     queryParam: 'query',
18432     /**
18433      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18434      * when mode = 'remote' (defaults to 'Loading...')
18435      */
18436     loadingText: 'Loading...',
18437     /**
18438      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18439      */
18440     resizable: false,
18441     /**
18442      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18443      */
18444     handleHeight : 8,
18445     /**
18446      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18447      * traditional select (defaults to true)
18448      */
18449     editable: true,
18450     /**
18451      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18452      */
18453     allQuery: '',
18454     /**
18455      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18456      */
18457     mode: 'remote',
18458     /**
18459      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18460      * listWidth has a higher value)
18461      */
18462     minListWidth : 70,
18463     /**
18464      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18465      * allow the user to set arbitrary text into the field (defaults to false)
18466      */
18467     forceSelection:false,
18468     /**
18469      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18470      * if typeAhead = true (defaults to 250)
18471      */
18472     typeAheadDelay : 250,
18473     /**
18474      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18475      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18476      */
18477     valueNotFoundText : undefined,
18478     /**
18479      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18480      */
18481     blockFocus : false,
18482     
18483     /**
18484      * @cfg {Boolean} disableClear Disable showing of clear button.
18485      */
18486     disableClear : false,
18487     /**
18488      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18489      */
18490     alwaysQuery : false,
18491     
18492     //private
18493     addicon : false,
18494     editicon: false,
18495     
18496     // element that contains real text value.. (when hidden is used..)
18497      
18498     // private
18499     onRender : function(ct, position)
18500     {
18501         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18502         
18503         if(this.hiddenName){
18504             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18505                     'before', true);
18506             this.hiddenField.value =
18507                 this.hiddenValue !== undefined ? this.hiddenValue :
18508                 this.value !== undefined ? this.value : '';
18509
18510             // prevent input submission
18511             this.el.dom.removeAttribute('name');
18512              
18513              
18514         }
18515         
18516         if(Roo.isGecko){
18517             this.el.dom.setAttribute('autocomplete', 'off');
18518         }
18519
18520         var cls = 'x-combo-list';
18521
18522         this.list = new Roo.Layer({
18523             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18524         });
18525
18526         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18527         this.list.setWidth(lw);
18528         this.list.swallowEvent('mousewheel');
18529         this.assetHeight = 0;
18530
18531         if(this.title){
18532             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18533             this.assetHeight += this.header.getHeight();
18534         }
18535
18536         this.innerList = this.list.createChild({cls:cls+'-inner'});
18537         this.innerList.on('mouseover', this.onViewOver, this);
18538         this.innerList.on('mousemove', this.onViewMove, this);
18539         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18540         
18541         if(this.allowBlank && !this.pageSize && !this.disableClear){
18542             this.footer = this.list.createChild({cls:cls+'-ft'});
18543             this.pageTb = new Roo.Toolbar(this.footer);
18544            
18545         }
18546         if(this.pageSize){
18547             this.footer = this.list.createChild({cls:cls+'-ft'});
18548             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18549                     {pageSize: this.pageSize});
18550             
18551         }
18552         
18553         if (this.pageTb && this.allowBlank && !this.disableClear) {
18554             var _this = this;
18555             this.pageTb.add(new Roo.Toolbar.Fill(), {
18556                 cls: 'x-btn-icon x-btn-clear',
18557                 text: '&#160;',
18558                 handler: function()
18559                 {
18560                     _this.collapse();
18561                     _this.clearValue();
18562                     _this.onSelect(false, -1);
18563                 }
18564             });
18565         }
18566         if (this.footer) {
18567             this.assetHeight += this.footer.getHeight();
18568         }
18569         
18570
18571         if(!this.tpl){
18572             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18573         }
18574
18575         this.view = new Roo.View(this.innerList, this.tpl, {
18576             singleSelect:true,
18577             store: this.store,
18578             selectedClass: this.selectedClass
18579         });
18580
18581         this.view.on('click', this.onViewClick, this);
18582
18583         this.store.on('beforeload', this.onBeforeLoad, this);
18584         this.store.on('load', this.onLoad, this);
18585         this.store.on('loadexception', this.onLoadException, this);
18586
18587         if(this.resizable){
18588             this.resizer = new Roo.Resizable(this.list,  {
18589                pinned:true, handles:'se'
18590             });
18591             this.resizer.on('resize', function(r, w, h){
18592                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18593                 this.listWidth = w;
18594                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18595                 this.restrictHeight();
18596             }, this);
18597             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18598         }
18599         if(!this.editable){
18600             this.editable = true;
18601             this.setEditable(false);
18602         }  
18603         
18604         
18605         if (typeof(this.events.add.listeners) != 'undefined') {
18606             
18607             this.addicon = this.wrap.createChild(
18608                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18609        
18610             this.addicon.on('click', function(e) {
18611                 this.fireEvent('add', this);
18612             }, this);
18613         }
18614         if (typeof(this.events.edit.listeners) != 'undefined') {
18615             
18616             this.editicon = this.wrap.createChild(
18617                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18618             if (this.addicon) {
18619                 this.editicon.setStyle('margin-left', '40px');
18620             }
18621             this.editicon.on('click', function(e) {
18622                 
18623                 // we fire even  if inothing is selected..
18624                 this.fireEvent('edit', this, this.lastData );
18625                 
18626             }, this);
18627         }
18628         
18629         
18630         
18631     },
18632
18633     // private
18634     initEvents : function(){
18635         Roo.form.ComboBox.superclass.initEvents.call(this);
18636
18637         this.keyNav = new Roo.KeyNav(this.el, {
18638             "up" : function(e){
18639                 this.inKeyMode = true;
18640                 this.selectPrev();
18641             },
18642
18643             "down" : function(e){
18644                 if(!this.isExpanded()){
18645                     this.onTriggerClick();
18646                 }else{
18647                     this.inKeyMode = true;
18648                     this.selectNext();
18649                 }
18650             },
18651
18652             "enter" : function(e){
18653                 this.onViewClick();
18654                 //return true;
18655             },
18656
18657             "esc" : function(e){
18658                 this.collapse();
18659             },
18660
18661             "tab" : function(e){
18662                 this.onViewClick(false);
18663                 this.fireEvent("specialkey", this, e);
18664                 return true;
18665             },
18666
18667             scope : this,
18668
18669             doRelay : function(foo, bar, hname){
18670                 if(hname == 'down' || this.scope.isExpanded()){
18671                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18672                 }
18673                 return true;
18674             },
18675
18676             forceKeyDown: true
18677         });
18678         this.queryDelay = Math.max(this.queryDelay || 10,
18679                 this.mode == 'local' ? 10 : 250);
18680         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18681         if(this.typeAhead){
18682             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18683         }
18684         if(this.editable !== false){
18685             this.el.on("keyup", this.onKeyUp, this);
18686         }
18687         if(this.forceSelection){
18688             this.on('blur', this.doForce, this);
18689         }
18690     },
18691
18692     onDestroy : function(){
18693         if(this.view){
18694             this.view.setStore(null);
18695             this.view.el.removeAllListeners();
18696             this.view.el.remove();
18697             this.view.purgeListeners();
18698         }
18699         if(this.list){
18700             this.list.destroy();
18701         }
18702         if(this.store){
18703             this.store.un('beforeload', this.onBeforeLoad, this);
18704             this.store.un('load', this.onLoad, this);
18705             this.store.un('loadexception', this.onLoadException, this);
18706         }
18707         Roo.form.ComboBox.superclass.onDestroy.call(this);
18708     },
18709
18710     // private
18711     fireKey : function(e){
18712         if(e.isNavKeyPress() && !this.list.isVisible()){
18713             this.fireEvent("specialkey", this, e);
18714         }
18715     },
18716
18717     // private
18718     onResize: function(w, h){
18719         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18720         
18721         if(typeof w != 'number'){
18722             // we do not handle it!?!?
18723             return;
18724         }
18725         var tw = this.trigger.getWidth();
18726         tw += this.addicon ? this.addicon.getWidth() : 0;
18727         tw += this.editicon ? this.editicon.getWidth() : 0;
18728         var x = w - tw;
18729         this.el.setWidth( this.adjustWidth('input', x));
18730             
18731         this.trigger.setStyle('left', x+'px');
18732         
18733         if(this.list && this.listWidth === undefined){
18734             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18735             this.list.setWidth(lw);
18736             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18737         }
18738         
18739     
18740         
18741     },
18742
18743     /**
18744      * Allow or prevent the user from directly editing the field text.  If false is passed,
18745      * the user will only be able to select from the items defined in the dropdown list.  This method
18746      * is the runtime equivalent of setting the 'editable' config option at config time.
18747      * @param {Boolean} value True to allow the user to directly edit the field text
18748      */
18749     setEditable : function(value){
18750         if(value == this.editable){
18751             return;
18752         }
18753         this.editable = value;
18754         if(!value){
18755             this.el.dom.setAttribute('readOnly', true);
18756             this.el.on('mousedown', this.onTriggerClick,  this);
18757             this.el.addClass('x-combo-noedit');
18758         }else{
18759             this.el.dom.setAttribute('readOnly', false);
18760             this.el.un('mousedown', this.onTriggerClick,  this);
18761             this.el.removeClass('x-combo-noedit');
18762         }
18763     },
18764
18765     // private
18766     onBeforeLoad : function(){
18767         if(!this.hasFocus){
18768             return;
18769         }
18770         this.innerList.update(this.loadingText ?
18771                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18772         this.restrictHeight();
18773         this.selectedIndex = -1;
18774     },
18775
18776     // private
18777     onLoad : function(){
18778         if(!this.hasFocus){
18779             return;
18780         }
18781         if(this.store.getCount() > 0){
18782             this.expand();
18783             this.restrictHeight();
18784             if(this.lastQuery == this.allQuery){
18785                 if(this.editable){
18786                     this.el.dom.select();
18787                 }
18788                 if(!this.selectByValue(this.value, true)){
18789                     this.select(0, true);
18790                 }
18791             }else{
18792                 this.selectNext();
18793                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18794                     this.taTask.delay(this.typeAheadDelay);
18795                 }
18796             }
18797         }else{
18798             this.onEmptyResults();
18799         }
18800         //this.el.focus();
18801     },
18802     // private
18803     onLoadException : function()
18804     {
18805         this.collapse();
18806         Roo.log(this.store.reader.jsonData);
18807         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18808             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18809         }
18810         
18811         
18812     },
18813     // private
18814     onTypeAhead : function(){
18815         if(this.store.getCount() > 0){
18816             var r = this.store.getAt(0);
18817             var newValue = r.data[this.displayField];
18818             var len = newValue.length;
18819             var selStart = this.getRawValue().length;
18820             if(selStart != len){
18821                 this.setRawValue(newValue);
18822                 this.selectText(selStart, newValue.length);
18823             }
18824         }
18825     },
18826
18827     // private
18828     onSelect : function(record, index){
18829         if(this.fireEvent('beforeselect', this, record, index) !== false){
18830             this.setFromData(index > -1 ? record.data : false);
18831             this.collapse();
18832             this.fireEvent('select', this, record, index);
18833         }
18834     },
18835
18836     /**
18837      * Returns the currently selected field value or empty string if no value is set.
18838      * @return {String} value The selected value
18839      */
18840     getValue : function(){
18841         if(this.valueField){
18842             return typeof this.value != 'undefined' ? this.value : '';
18843         }
18844         return Roo.form.ComboBox.superclass.getValue.call(this);
18845     },
18846
18847     /**
18848      * Clears any text/value currently set in the field
18849      */
18850     clearValue : function(){
18851         if(this.hiddenField){
18852             this.hiddenField.value = '';
18853         }
18854         this.value = '';
18855         this.setRawValue('');
18856         this.lastSelectionText = '';
18857         
18858     },
18859
18860     /**
18861      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18862      * will be displayed in the field.  If the value does not match the data value of an existing item,
18863      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18864      * Otherwise the field will be blank (although the value will still be set).
18865      * @param {String} value The value to match
18866      */
18867     setValue : function(v){
18868         var text = v;
18869         if(this.valueField){
18870             var r = this.findRecord(this.valueField, v);
18871             if(r){
18872                 text = r.data[this.displayField];
18873             }else if(this.valueNotFoundText !== undefined){
18874                 text = this.valueNotFoundText;
18875             }
18876         }
18877         this.lastSelectionText = text;
18878         if(this.hiddenField){
18879             this.hiddenField.value = v;
18880         }
18881         Roo.form.ComboBox.superclass.setValue.call(this, text);
18882         this.value = v;
18883     },
18884     /**
18885      * @property {Object} the last set data for the element
18886      */
18887     
18888     lastData : false,
18889     /**
18890      * Sets the value of the field based on a object which is related to the record format for the store.
18891      * @param {Object} value the value to set as. or false on reset?
18892      */
18893     setFromData : function(o){
18894         var dv = ''; // display value
18895         var vv = ''; // value value..
18896         this.lastData = o;
18897         if (this.displayField) {
18898             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18899         } else {
18900             // this is an error condition!!!
18901             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18902         }
18903         
18904         if(this.valueField){
18905             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18906         }
18907         if(this.hiddenField){
18908             this.hiddenField.value = vv;
18909             
18910             this.lastSelectionText = dv;
18911             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18912             this.value = vv;
18913             return;
18914         }
18915         // no hidden field.. - we store the value in 'value', but still display
18916         // display field!!!!
18917         this.lastSelectionText = dv;
18918         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18919         this.value = vv;
18920         
18921         
18922     },
18923     // private
18924     reset : function(){
18925         // overridden so that last data is reset..
18926         this.setValue(this.resetValue);
18927         this.originalValue = this.getValue();
18928         this.clearInvalid();
18929         this.lastData = false;
18930         if (this.view) {
18931             this.view.clearSelections();
18932         }
18933     },
18934     // private
18935     findRecord : function(prop, value){
18936         var record;
18937         if(this.store.getCount() > 0){
18938             this.store.each(function(r){
18939                 if(r.data[prop] == value){
18940                     record = r;
18941                     return false;
18942                 }
18943                 return true;
18944             });
18945         }
18946         return record;
18947     },
18948     
18949     getName: function()
18950     {
18951         // returns hidden if it's set..
18952         if (!this.rendered) {return ''};
18953         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18954         
18955     },
18956     // private
18957     onViewMove : function(e, t){
18958         this.inKeyMode = false;
18959     },
18960
18961     // private
18962     onViewOver : function(e, t){
18963         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18964             return;
18965         }
18966         var item = this.view.findItemFromChild(t);
18967         if(item){
18968             var index = this.view.indexOf(item);
18969             this.select(index, false);
18970         }
18971     },
18972
18973     // private
18974     onViewClick : function(doFocus)
18975     {
18976         var index = this.view.getSelectedIndexes()[0];
18977         var r = this.store.getAt(index);
18978         if(r){
18979             this.onSelect(r, index);
18980         }
18981         if(doFocus !== false && !this.blockFocus){
18982             this.el.focus();
18983         }
18984     },
18985
18986     // private
18987     restrictHeight : function(){
18988         this.innerList.dom.style.height = '';
18989         var inner = this.innerList.dom;
18990         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18991         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18992         this.list.beginUpdate();
18993         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18994         this.list.alignTo(this.el, this.listAlign);
18995         this.list.endUpdate();
18996     },
18997
18998     // private
18999     onEmptyResults : function(){
19000         this.collapse();
19001     },
19002
19003     /**
19004      * Returns true if the dropdown list is expanded, else false.
19005      */
19006     isExpanded : function(){
19007         return this.list.isVisible();
19008     },
19009
19010     /**
19011      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19012      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19013      * @param {String} value The data value of the item to select
19014      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19015      * selected item if it is not currently in view (defaults to true)
19016      * @return {Boolean} True if the value matched an item in the list, else false
19017      */
19018     selectByValue : function(v, scrollIntoView){
19019         if(v !== undefined && v !== null){
19020             var r = this.findRecord(this.valueField || this.displayField, v);
19021             if(r){
19022                 this.select(this.store.indexOf(r), scrollIntoView);
19023                 return true;
19024             }
19025         }
19026         return false;
19027     },
19028
19029     /**
19030      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19031      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19032      * @param {Number} index The zero-based index of the list item to select
19033      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19034      * selected item if it is not currently in view (defaults to true)
19035      */
19036     select : function(index, scrollIntoView){
19037         this.selectedIndex = index;
19038         this.view.select(index);
19039         if(scrollIntoView !== false){
19040             var el = this.view.getNode(index);
19041             if(el){
19042                 this.innerList.scrollChildIntoView(el, false);
19043             }
19044         }
19045     },
19046
19047     // private
19048     selectNext : function(){
19049         var ct = this.store.getCount();
19050         if(ct > 0){
19051             if(this.selectedIndex == -1){
19052                 this.select(0);
19053             }else if(this.selectedIndex < ct-1){
19054                 this.select(this.selectedIndex+1);
19055             }
19056         }
19057     },
19058
19059     // private
19060     selectPrev : function(){
19061         var ct = this.store.getCount();
19062         if(ct > 0){
19063             if(this.selectedIndex == -1){
19064                 this.select(0);
19065             }else if(this.selectedIndex != 0){
19066                 this.select(this.selectedIndex-1);
19067             }
19068         }
19069     },
19070
19071     // private
19072     onKeyUp : function(e){
19073         if(this.editable !== false && !e.isSpecialKey()){
19074             this.lastKey = e.getKey();
19075             this.dqTask.delay(this.queryDelay);
19076         }
19077     },
19078
19079     // private
19080     validateBlur : function(){
19081         return !this.list || !this.list.isVisible();   
19082     },
19083
19084     // private
19085     initQuery : function(){
19086         this.doQuery(this.getRawValue());
19087     },
19088
19089     // private
19090     doForce : function(){
19091         if(this.el.dom.value.length > 0){
19092             this.el.dom.value =
19093                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19094              
19095         }
19096     },
19097
19098     /**
19099      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19100      * query allowing the query action to be canceled if needed.
19101      * @param {String} query The SQL query to execute
19102      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19103      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19104      * saved in the current store (defaults to false)
19105      */
19106     doQuery : function(q, forceAll){
19107         if(q === undefined || q === null){
19108             q = '';
19109         }
19110         var qe = {
19111             query: q,
19112             forceAll: forceAll,
19113             combo: this,
19114             cancel:false
19115         };
19116         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19117             return false;
19118         }
19119         q = qe.query;
19120         forceAll = qe.forceAll;
19121         if(forceAll === true || (q.length >= this.minChars)){
19122             if(this.lastQuery != q || this.alwaysQuery){
19123                 this.lastQuery = q;
19124                 if(this.mode == 'local'){
19125                     this.selectedIndex = -1;
19126                     if(forceAll){
19127                         this.store.clearFilter();
19128                     }else{
19129                         this.store.filter(this.displayField, q);
19130                     }
19131                     this.onLoad();
19132                 }else{
19133                     this.store.baseParams[this.queryParam] = q;
19134                     this.store.load({
19135                         params: this.getParams(q)
19136                     });
19137                     this.expand();
19138                 }
19139             }else{
19140                 this.selectedIndex = -1;
19141                 this.onLoad();   
19142             }
19143         }
19144     },
19145
19146     // private
19147     getParams : function(q){
19148         var p = {};
19149         //p[this.queryParam] = q;
19150         if(this.pageSize){
19151             p.start = 0;
19152             p.limit = this.pageSize;
19153         }
19154         return p;
19155     },
19156
19157     /**
19158      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19159      */
19160     collapse : function(){
19161         if(!this.isExpanded()){
19162             return;
19163         }
19164         this.list.hide();
19165         Roo.get(document).un('mousedown', this.collapseIf, this);
19166         Roo.get(document).un('mousewheel', this.collapseIf, this);
19167         if (!this.editable) {
19168             Roo.get(document).un('keydown', this.listKeyPress, this);
19169         }
19170         this.fireEvent('collapse', this);
19171     },
19172
19173     // private
19174     collapseIf : function(e){
19175         if(!e.within(this.wrap) && !e.within(this.list)){
19176             this.collapse();
19177         }
19178     },
19179
19180     /**
19181      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19182      */
19183     expand : function(){
19184         if(this.isExpanded() || !this.hasFocus){
19185             return;
19186         }
19187         this.list.alignTo(this.el, this.listAlign);
19188         this.list.show();
19189         Roo.get(document).on('mousedown', this.collapseIf, this);
19190         Roo.get(document).on('mousewheel', this.collapseIf, this);
19191         if (!this.editable) {
19192             Roo.get(document).on('keydown', this.listKeyPress, this);
19193         }
19194         
19195         this.fireEvent('expand', this);
19196     },
19197
19198     // private
19199     // Implements the default empty TriggerField.onTriggerClick function
19200     onTriggerClick : function(){
19201         if(this.disabled){
19202             return;
19203         }
19204         if(this.isExpanded()){
19205             this.collapse();
19206             if (!this.blockFocus) {
19207                 this.el.focus();
19208             }
19209             
19210         }else {
19211             this.hasFocus = true;
19212             if(this.triggerAction == 'all') {
19213                 this.doQuery(this.allQuery, true);
19214             } else {
19215                 this.doQuery(this.getRawValue());
19216             }
19217             if (!this.blockFocus) {
19218                 this.el.focus();
19219             }
19220         }
19221     },
19222     listKeyPress : function(e)
19223     {
19224         //Roo.log('listkeypress');
19225         // scroll to first matching element based on key pres..
19226         if (e.isSpecialKey()) {
19227             return false;
19228         }
19229         var k = String.fromCharCode(e.getKey()).toUpperCase();
19230         //Roo.log(k);
19231         var match  = false;
19232         var csel = this.view.getSelectedNodes();
19233         var cselitem = false;
19234         if (csel.length) {
19235             var ix = this.view.indexOf(csel[0]);
19236             cselitem  = this.store.getAt(ix);
19237             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19238                 cselitem = false;
19239             }
19240             
19241         }
19242         
19243         this.store.each(function(v) { 
19244             if (cselitem) {
19245                 // start at existing selection.
19246                 if (cselitem.id == v.id) {
19247                     cselitem = false;
19248                 }
19249                 return;
19250             }
19251                 
19252             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19253                 match = this.store.indexOf(v);
19254                 return false;
19255             }
19256         }, this);
19257         
19258         if (match === false) {
19259             return true; // no more action?
19260         }
19261         // scroll to?
19262         this.view.select(match);
19263         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19264         sn.scrollIntoView(sn.dom.parentNode, false);
19265     } 
19266
19267     /** 
19268     * @cfg {Boolean} grow 
19269     * @hide 
19270     */
19271     /** 
19272     * @cfg {Number} growMin 
19273     * @hide 
19274     */
19275     /** 
19276     * @cfg {Number} growMax 
19277     * @hide 
19278     */
19279     /**
19280      * @hide
19281      * @method autoSize
19282      */
19283 });/*
19284  * Copyright(c) 2010-2012, Roo J Solutions Limited
19285  *
19286  * Licence LGPL
19287  *
19288  */
19289
19290 /**
19291  * @class Roo.form.ComboBoxArray
19292  * @extends Roo.form.TextField
19293  * A facebook style adder... for lists of email / people / countries  etc...
19294  * pick multiple items from a combo box, and shows each one.
19295  *
19296  *  Fred [x]  Brian [x]  [Pick another |v]
19297  *
19298  *
19299  *  For this to work: it needs various extra information
19300  *    - normal combo problay has
19301  *      name, hiddenName
19302  *    + displayField, valueField
19303  *
19304  *    For our purpose...
19305  *
19306  *
19307  *   If we change from 'extends' to wrapping...
19308  *   
19309  *  
19310  *
19311  
19312  
19313  * @constructor
19314  * Create a new ComboBoxArray.
19315  * @param {Object} config Configuration options
19316  */
19317  
19318
19319 Roo.form.ComboBoxArray = function(config)
19320 {
19321     this.addEvents({
19322         /**
19323          * @event beforeremove
19324          * Fires before remove the value from the list
19325              * @param {Roo.form.ComboBoxArray} _self This combo box array
19326              * @param {Roo.form.ComboBoxArray.Item} item removed item
19327              */
19328         'beforeremove' : true,
19329         /**
19330          * @event remove
19331          * Fires when remove the value from the list
19332              * @param {Roo.form.ComboBoxArray} _self This combo box array
19333              * @param {Roo.form.ComboBoxArray.Item} item removed item
19334              */
19335         'remove' : true
19336         
19337         
19338     });
19339     
19340     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19341     
19342     this.items = new Roo.util.MixedCollection(false);
19343     
19344     // construct the child combo...
19345     
19346     
19347     
19348     
19349    
19350     
19351 }
19352
19353  
19354 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19355
19356     /**
19357      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19358      */
19359     
19360     lastData : false,
19361     
19362     // behavies liek a hiddne field
19363     inputType:      'hidden',
19364     /**
19365      * @cfg {Number} width The width of the box that displays the selected element
19366      */ 
19367     width:          300,
19368
19369     
19370     
19371     /**
19372      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19373      */
19374     name : false,
19375     /**
19376      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19377      */
19378     hiddenName : false,
19379       /**
19380      * @cfg {String} seperator    The value seperator normally ',' 
19381      */
19382     seperator : ',',
19383     
19384     // private the array of items that are displayed..
19385     items  : false,
19386     // private - the hidden field el.
19387     hiddenEl : false,
19388     // private - the filed el..
19389     el : false,
19390     
19391     //validateValue : function() { return true; }, // all values are ok!
19392     //onAddClick: function() { },
19393     
19394     onRender : function(ct, position) 
19395     {
19396         
19397         // create the standard hidden element
19398         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19399         
19400         
19401         // give fake names to child combo;
19402         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19403         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19404         
19405         this.combo = Roo.factory(this.combo, Roo.form);
19406         this.combo.onRender(ct, position);
19407         if (typeof(this.combo.width) != 'undefined') {
19408             this.combo.onResize(this.combo.width,0);
19409         }
19410         
19411         this.combo.initEvents();
19412         
19413         // assigned so form know we need to do this..
19414         this.store          = this.combo.store;
19415         this.valueField     = this.combo.valueField;
19416         this.displayField   = this.combo.displayField ;
19417         
19418         
19419         this.combo.wrap.addClass('x-cbarray-grp');
19420         
19421         var cbwrap = this.combo.wrap.createChild(
19422             {tag: 'div', cls: 'x-cbarray-cb'},
19423             this.combo.el.dom
19424         );
19425         
19426              
19427         this.hiddenEl = this.combo.wrap.createChild({
19428             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19429         });
19430         this.el = this.combo.wrap.createChild({
19431             tag: 'input',  type:'hidden' , name: this.name, value : ''
19432         });
19433          //   this.el.dom.removeAttribute("name");
19434         
19435         
19436         this.outerWrap = this.combo.wrap;
19437         this.wrap = cbwrap;
19438         
19439         this.outerWrap.setWidth(this.width);
19440         this.outerWrap.dom.removeChild(this.el.dom);
19441         
19442         this.wrap.dom.appendChild(this.el.dom);
19443         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19444         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19445         
19446         this.combo.trigger.setStyle('position','relative');
19447         this.combo.trigger.setStyle('left', '0px');
19448         this.combo.trigger.setStyle('top', '2px');
19449         
19450         this.combo.el.setStyle('vertical-align', 'text-bottom');
19451         
19452         //this.trigger.setStyle('vertical-align', 'top');
19453         
19454         // this should use the code from combo really... on('add' ....)
19455         if (this.adder) {
19456             
19457         
19458             this.adder = this.outerWrap.createChild(
19459                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19460             var _t = this;
19461             this.adder.on('click', function(e) {
19462                 _t.fireEvent('adderclick', this, e);
19463             }, _t);
19464         }
19465         //var _t = this;
19466         //this.adder.on('click', this.onAddClick, _t);
19467         
19468         
19469         this.combo.on('select', function(cb, rec, ix) {
19470             this.addItem(rec.data);
19471             
19472             cb.setValue('');
19473             cb.el.dom.value = '';
19474             //cb.lastData = rec.data;
19475             // add to list
19476             
19477         }, this);
19478         
19479         
19480     },
19481     
19482     
19483     getName: function()
19484     {
19485         // returns hidden if it's set..
19486         if (!this.rendered) {return ''};
19487         return  this.hiddenName ? this.hiddenName : this.name;
19488         
19489     },
19490     
19491     
19492     onResize: function(w, h){
19493         
19494         return;
19495         // not sure if this is needed..
19496         //this.combo.onResize(w,h);
19497         
19498         if(typeof w != 'number'){
19499             // we do not handle it!?!?
19500             return;
19501         }
19502         var tw = this.combo.trigger.getWidth();
19503         tw += this.addicon ? this.addicon.getWidth() : 0;
19504         tw += this.editicon ? this.editicon.getWidth() : 0;
19505         var x = w - tw;
19506         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19507             
19508         this.combo.trigger.setStyle('left', '0px');
19509         
19510         if(this.list && this.listWidth === undefined){
19511             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19512             this.list.setWidth(lw);
19513             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19514         }
19515         
19516     
19517         
19518     },
19519     
19520     addItem: function(rec)
19521     {
19522         var valueField = this.combo.valueField;
19523         var displayField = this.combo.displayField;
19524         
19525         if (this.items.indexOfKey(rec[valueField]) > -1) {
19526             //console.log("GOT " + rec.data.id);
19527             return;
19528         }
19529         
19530         var x = new Roo.form.ComboBoxArray.Item({
19531             //id : rec[this.idField],
19532             data : rec,
19533             displayField : displayField ,
19534             tipField : displayField ,
19535             cb : this
19536         });
19537         // use the 
19538         this.items.add(rec[valueField],x);
19539         // add it before the element..
19540         this.updateHiddenEl();
19541         x.render(this.outerWrap, this.wrap.dom);
19542         // add the image handler..
19543     },
19544     
19545     updateHiddenEl : function()
19546     {
19547         this.validate();
19548         if (!this.hiddenEl) {
19549             return;
19550         }
19551         var ar = [];
19552         var idField = this.combo.valueField;
19553         
19554         this.items.each(function(f) {
19555             ar.push(f.data[idField]);
19556         });
19557         this.hiddenEl.dom.value = ar.join(this.seperator);
19558         this.validate();
19559     },
19560     
19561     reset : function()
19562     {
19563         this.items.clear();
19564         
19565         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19566            el.remove();
19567         });
19568         
19569         this.el.dom.value = '';
19570         if (this.hiddenEl) {
19571             this.hiddenEl.dom.value = '';
19572         }
19573         
19574     },
19575     getValue: function()
19576     {
19577         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19578     },
19579     setValue: function(v) // not a valid action - must use addItems..
19580     {
19581         
19582         this.reset();
19583          
19584         if (this.store.isLocal && (typeof(v) == 'string')) {
19585             // then we can use the store to find the values..
19586             // comma seperated at present.. this needs to allow JSON based encoding..
19587             this.hiddenEl.value  = v;
19588             var v_ar = [];
19589             Roo.each(v.split(this.seperator), function(k) {
19590                 Roo.log("CHECK " + this.valueField + ',' + k);
19591                 var li = this.store.query(this.valueField, k);
19592                 if (!li.length) {
19593                     return;
19594                 }
19595                 var add = {};
19596                 add[this.valueField] = k;
19597                 add[this.displayField] = li.item(0).data[this.displayField];
19598                 
19599                 this.addItem(add);
19600             }, this) 
19601              
19602         }
19603         if (typeof(v) == 'object' ) {
19604             // then let's assume it's an array of objects..
19605             Roo.each(v, function(l) {
19606                 var add = l;
19607                 if (typeof(l) == 'string') {
19608                     add = {};
19609                     add[this.valueField] = l;
19610                     add[this.displayField] = l
19611                 }
19612                 this.addItem(add);
19613             }, this);
19614              
19615         }
19616         
19617         
19618     },
19619     setFromData: function(v)
19620     {
19621         // this recieves an object, if setValues is called.
19622         this.reset();
19623         this.el.dom.value = v[this.displayField];
19624         this.hiddenEl.dom.value = v[this.valueField];
19625         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19626             return;
19627         }
19628         var kv = v[this.valueField];
19629         var dv = v[this.displayField];
19630         kv = typeof(kv) != 'string' ? '' : kv;
19631         dv = typeof(dv) != 'string' ? '' : dv;
19632         
19633         
19634         var keys = kv.split(this.seperator);
19635         var display = dv.split(this.seperator);
19636         for (var i = 0 ; i < keys.length; i++) {
19637             add = {};
19638             add[this.valueField] = keys[i];
19639             add[this.displayField] = display[i];
19640             this.addItem(add);
19641         }
19642       
19643         
19644     },
19645     
19646     /**
19647      * Validates the combox array value
19648      * @return {Boolean} True if the value is valid, else false
19649      */
19650     validate : function(){
19651         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19652             this.clearInvalid();
19653             return true;
19654         }
19655         return false;
19656     },
19657     
19658     validateValue : function(value){
19659         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19660         
19661     },
19662     
19663     /*@
19664      * overide
19665      * 
19666      */
19667     isDirty : function() {
19668         if(this.disabled) {
19669             return false;
19670         }
19671         
19672         try {
19673             var d = Roo.decode(String(this.originalValue));
19674         } catch (e) {
19675             return String(this.getValue()) !== String(this.originalValue);
19676         }
19677         
19678         var originalValue = [];
19679         
19680         for (var i = 0; i < d.length; i++){
19681             originalValue.push(d[i][this.valueField]);
19682         }
19683         
19684         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19685         
19686     }
19687     
19688 });
19689
19690
19691
19692 /**
19693  * @class Roo.form.ComboBoxArray.Item
19694  * @extends Roo.BoxComponent
19695  * A selected item in the list
19696  *  Fred [x]  Brian [x]  [Pick another |v]
19697  * 
19698  * @constructor
19699  * Create a new item.
19700  * @param {Object} config Configuration options
19701  */
19702  
19703 Roo.form.ComboBoxArray.Item = function(config) {
19704     config.id = Roo.id();
19705     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19706 }
19707
19708 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19709     data : {},
19710     cb: false,
19711     displayField : false,
19712     tipField : false,
19713     
19714     
19715     defaultAutoCreate : {
19716         tag: 'div',
19717         cls: 'x-cbarray-item',
19718         cn : [ 
19719             { tag: 'div' },
19720             {
19721                 tag: 'img',
19722                 width:16,
19723                 height : 16,
19724                 src : Roo.BLANK_IMAGE_URL ,
19725                 align: 'center'
19726             }
19727         ]
19728         
19729     },
19730     
19731  
19732     onRender : function(ct, position)
19733     {
19734         Roo.form.Field.superclass.onRender.call(this, ct, position);
19735         
19736         if(!this.el){
19737             var cfg = this.getAutoCreate();
19738             this.el = ct.createChild(cfg, position);
19739         }
19740         
19741         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19742         
19743         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19744             this.cb.renderer(this.data) :
19745             String.format('{0}',this.data[this.displayField]);
19746         
19747             
19748         this.el.child('div').dom.setAttribute('qtip',
19749                         String.format('{0}',this.data[this.tipField])
19750         );
19751         
19752         this.el.child('img').on('click', this.remove, this);
19753         
19754     },
19755    
19756     remove : function()
19757     {
19758         if(this.cb.disabled){
19759             return;
19760         }
19761         
19762         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19763             this.cb.items.remove(this);
19764             this.el.child('img').un('click', this.remove, this);
19765             this.el.remove();
19766             this.cb.updateHiddenEl();
19767
19768             this.cb.fireEvent('remove', this.cb, this);
19769         }
19770         
19771     }
19772 });/*
19773  * RooJS Library 1.1.1
19774  * Copyright(c) 2008-2011  Alan Knowles
19775  *
19776  * License - LGPL
19777  */
19778  
19779
19780 /**
19781  * @class Roo.form.ComboNested
19782  * @extends Roo.form.ComboBox
19783  * A combobox for that allows selection of nested items in a list,
19784  * eg.
19785  *
19786  *  Book
19787  *    -> red
19788  *    -> green
19789  *  Table
19790  *    -> square
19791  *      ->red
19792  *      ->green
19793  *    -> rectangle
19794  *      ->green
19795  *      
19796  * 
19797  * @constructor
19798  * Create a new ComboNested
19799  * @param {Object} config Configuration options
19800  */
19801 Roo.form.ComboNested = function(config){
19802     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19803     // should verify some data...
19804     // like
19805     // hiddenName = required..
19806     // displayField = required
19807     // valudField == required
19808     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19809     var _t = this;
19810     Roo.each(req, function(e) {
19811         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19812             throw "Roo.form.ComboNested : missing value for: " + e;
19813         }
19814     });
19815      
19816     
19817 };
19818
19819 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19820    
19821     /*
19822      * @config {Number} max Number of columns to show
19823      */
19824     
19825     maxColumns : 3,
19826    
19827     list : null, // the outermost div..
19828     innerLists : null, // the
19829     views : null,
19830     stores : null,
19831     // private
19832     loadingChildren : false,
19833     
19834     onRender : function(ct, position)
19835     {
19836         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19837         
19838         if(this.hiddenName){
19839             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19840                     'before', true);
19841             this.hiddenField.value =
19842                 this.hiddenValue !== undefined ? this.hiddenValue :
19843                 this.value !== undefined ? this.value : '';
19844
19845             // prevent input submission
19846             this.el.dom.removeAttribute('name');
19847              
19848              
19849         }
19850         
19851         if(Roo.isGecko){
19852             this.el.dom.setAttribute('autocomplete', 'off');
19853         }
19854
19855         var cls = 'x-combo-list';
19856
19857         this.list = new Roo.Layer({
19858             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19859         });
19860
19861         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19862         this.list.setWidth(lw);
19863         this.list.swallowEvent('mousewheel');
19864         this.assetHeight = 0;
19865
19866         if(this.title){
19867             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19868             this.assetHeight += this.header.getHeight();
19869         }
19870         this.innerLists = [];
19871         this.views = [];
19872         this.stores = [];
19873         for (var i =0 ; i < this.maxColumns; i++) {
19874             this.onRenderList( cls, i);
19875         }
19876         
19877         // always needs footer, as we are going to have an 'OK' button.
19878         this.footer = this.list.createChild({cls:cls+'-ft'});
19879         this.pageTb = new Roo.Toolbar(this.footer);  
19880         var _this = this;
19881         this.pageTb.add(  {
19882             
19883             text: 'Done',
19884             handler: function()
19885             {
19886                 _this.collapse();
19887             }
19888         });
19889         
19890         if ( this.allowBlank && !this.disableClear) {
19891             
19892             this.pageTb.add(new Roo.Toolbar.Fill(), {
19893                 cls: 'x-btn-icon x-btn-clear',
19894                 text: '&#160;',
19895                 handler: function()
19896                 {
19897                     _this.collapse();
19898                     _this.clearValue();
19899                     _this.onSelect(false, -1);
19900                 }
19901             });
19902         }
19903         if (this.footer) {
19904             this.assetHeight += this.footer.getHeight();
19905         }
19906         
19907     },
19908     onRenderList : function (  cls, i)
19909     {
19910         
19911         var lw = Math.floor(
19912                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19913         );
19914         
19915         this.list.setWidth(lw); // default to '1'
19916
19917         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19918         //il.on('mouseover', this.onViewOver, this, { list:  i });
19919         //il.on('mousemove', this.onViewMove, this, { list:  i });
19920         il.setWidth(lw);
19921         il.setStyle({ 'overflow-x' : 'hidden'});
19922
19923         if(!this.tpl){
19924             this.tpl = new Roo.Template({
19925                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19926                 isEmpty: function (value, allValues) {
19927                     //Roo.log(value);
19928                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19929                     return dl ? 'has-children' : 'no-children'
19930                 }
19931             });
19932         }
19933         
19934         var store  = this.store;
19935         if (i > 0) {
19936             store  = new Roo.data.SimpleStore({
19937                 //fields : this.store.reader.meta.fields,
19938                 reader : this.store.reader,
19939                 data : [ ]
19940             });
19941         }
19942         this.stores[i]  = store;
19943                   
19944         var view = this.views[i] = new Roo.View(
19945             il,
19946             this.tpl,
19947             {
19948                 singleSelect:true,
19949                 store: store,
19950                 selectedClass: this.selectedClass
19951             }
19952         );
19953         view.getEl().setWidth(lw);
19954         view.getEl().setStyle({
19955             position: i < 1 ? 'relative' : 'absolute',
19956             top: 0,
19957             left: (i * lw ) + 'px',
19958             display : i > 0 ? 'none' : 'block'
19959         });
19960         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
19961         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
19962         //view.on('click', this.onViewClick, this, { list : i });
19963
19964         store.on('beforeload', this.onBeforeLoad, this);
19965         store.on('load',  this.onLoad, this, { list  : i});
19966         store.on('loadexception', this.onLoadException, this);
19967
19968         // hide the other vies..
19969         
19970         
19971         
19972     },
19973       
19974     restrictHeight : function()
19975     {
19976         var mh = 0;
19977         Roo.each(this.innerLists, function(il,i) {
19978             var el = this.views[i].getEl();
19979             el.dom.style.height = '';
19980             var inner = el.dom;
19981             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
19982             // only adjust heights on other ones..
19983             mh = Math.max(h, mh);
19984             if (i < 1) {
19985                 
19986                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19987                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19988                
19989             }
19990             
19991             
19992         }, this);
19993         
19994         this.list.beginUpdate();
19995         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
19996         this.list.alignTo(this.el, this.listAlign);
19997         this.list.endUpdate();
19998         
19999     },
20000      
20001     
20002     // -- store handlers..
20003     // private
20004     onBeforeLoad : function()
20005     {
20006         if(!this.hasFocus){
20007             return;
20008         }
20009         this.innerLists[0].update(this.loadingText ?
20010                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20011         this.restrictHeight();
20012         this.selectedIndex = -1;
20013     },
20014     // private
20015     onLoad : function(a,b,c,d)
20016     {
20017         if (!this.loadingChildren) {
20018             // then we are loading the top level. - hide the children
20019             for (var i = 1;i < this.views.length; i++) {
20020                 this.views[i].getEl().setStyle({ display : 'none' });
20021             }
20022             var lw = Math.floor(
20023                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20024             );
20025         
20026              this.list.setWidth(lw); // default to '1'
20027
20028             
20029         }
20030         if(!this.hasFocus){
20031             return;
20032         }
20033         
20034         if(this.store.getCount() > 0) {
20035             this.expand();
20036             this.restrictHeight();   
20037         } else {
20038             this.onEmptyResults();
20039         }
20040         
20041         if (!this.loadingChildren) {
20042             this.selectActive();
20043         }
20044         /*
20045         this.stores[1].loadData([]);
20046         this.stores[2].loadData([]);
20047         this.views
20048         */    
20049     
20050         //this.el.focus();
20051     },
20052     
20053     
20054     // private
20055     onLoadException : function()
20056     {
20057         this.collapse();
20058         Roo.log(this.store.reader.jsonData);
20059         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20060             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20061         }
20062         
20063         
20064     },
20065     // no cleaning of leading spaces on blur here.
20066     cleanLeadingSpace : function(e) { },
20067     
20068
20069     onSelectChange : function (view, sels, opts )
20070     {
20071         var ix = view.getSelectedIndexes();
20072          
20073         if (opts.list > this.maxColumns - 2) {
20074             if (view.store.getCount()<  1) {
20075                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20076
20077             } else  {
20078                 if (ix.length) {
20079                     // used to clear ?? but if we are loading unselected 
20080                     this.setFromData(view.store.getAt(ix[0]).data);
20081                 }
20082                 
20083             }
20084             
20085             return;
20086         }
20087         
20088         if (!ix.length) {
20089             // this get's fired when trigger opens..
20090            // this.setFromData({});
20091             var str = this.stores[opts.list+1];
20092             str.data.clear(); // removeall wihtout the fire events..
20093             return;
20094         }
20095         
20096         var rec = view.store.getAt(ix[0]);
20097          
20098         this.setFromData(rec.data);
20099         this.fireEvent('select', this, rec, ix[0]);
20100         
20101         var lw = Math.floor(
20102              (
20103                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20104              ) / this.maxColumns
20105         );
20106         this.loadingChildren = true;
20107         this.stores[opts.list+1].loadDataFromChildren( rec );
20108         this.loadingChildren = false;
20109         var dl = this.stores[opts.list+1]. getTotalCount();
20110         
20111         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20112         
20113         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20114         for (var i = opts.list+2; i < this.views.length;i++) {
20115             this.views[i].getEl().setStyle({ display : 'none' });
20116         }
20117         
20118         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20119         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20120         
20121         if (this.isLoading) {
20122            // this.selectActive(opts.list);
20123         }
20124          
20125     },
20126     
20127     
20128     
20129     
20130     onDoubleClick : function()
20131     {
20132         this.collapse(); //??
20133     },
20134     
20135      
20136     
20137     
20138     
20139     // private
20140     recordToStack : function(store, prop, value, stack)
20141     {
20142         var cstore = new Roo.data.SimpleStore({
20143             //fields : this.store.reader.meta.fields, // we need array reader.. for
20144             reader : this.store.reader,
20145             data : [ ]
20146         });
20147         var _this = this;
20148         var record  = false;
20149         var srec = false;
20150         if(store.getCount() < 1){
20151             return false;
20152         }
20153         store.each(function(r){
20154             if(r.data[prop] == value){
20155                 record = r;
20156             srec = r;
20157                 return false;
20158             }
20159             if (r.data.cn && r.data.cn.length) {
20160                 cstore.loadDataFromChildren( r);
20161                 var cret = _this.recordToStack(cstore, prop, value, stack);
20162                 if (cret !== false) {
20163                     record = cret;
20164                     srec = r;
20165                     return false;
20166                 }
20167             }
20168              
20169             return true;
20170         });
20171         if (record == false) {
20172             return false
20173         }
20174         stack.unshift(srec);
20175         return record;
20176     },
20177     
20178     /*
20179      * find the stack of stores that match our value.
20180      *
20181      * 
20182      */
20183     
20184     selectActive : function ()
20185     {
20186         // if store is not loaded, then we will need to wait for that to happen first.
20187         var stack = [];
20188         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20189         for (var i = 0; i < stack.length; i++ ) {
20190             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20191         }
20192         
20193     }
20194         
20195          
20196     
20197     
20198     
20199     
20200 });/*
20201  * Based on:
20202  * Ext JS Library 1.1.1
20203  * Copyright(c) 2006-2007, Ext JS, LLC.
20204  *
20205  * Originally Released Under LGPL - original licence link has changed is not relivant.
20206  *
20207  * Fork - LGPL
20208  * <script type="text/javascript">
20209  */
20210 /**
20211  * @class Roo.form.Checkbox
20212  * @extends Roo.form.Field
20213  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20214  * @constructor
20215  * Creates a new Checkbox
20216  * @param {Object} config Configuration options
20217  */
20218 Roo.form.Checkbox = function(config){
20219     Roo.form.Checkbox.superclass.constructor.call(this, config);
20220     this.addEvents({
20221         /**
20222          * @event check
20223          * Fires when the checkbox is checked or unchecked.
20224              * @param {Roo.form.Checkbox} this This checkbox
20225              * @param {Boolean} checked The new checked value
20226              */
20227         check : true
20228     });
20229 };
20230
20231 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20232     /**
20233      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20234      */
20235     focusClass : undefined,
20236     /**
20237      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20238      */
20239     fieldClass: "x-form-field",
20240     /**
20241      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20242      */
20243     checked: false,
20244     /**
20245      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20246      * {tag: "input", type: "checkbox", autocomplete: "off"})
20247      */
20248     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20249     /**
20250      * @cfg {String} boxLabel The text that appears beside the checkbox
20251      */
20252     boxLabel : "",
20253     /**
20254      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20255      */  
20256     inputValue : '1',
20257     /**
20258      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20259      */
20260      valueOff: '0', // value when not checked..
20261
20262     actionMode : 'viewEl', 
20263     //
20264     // private
20265     itemCls : 'x-menu-check-item x-form-item',
20266     groupClass : 'x-menu-group-item',
20267     inputType : 'hidden',
20268     
20269     
20270     inSetChecked: false, // check that we are not calling self...
20271     
20272     inputElement: false, // real input element?
20273     basedOn: false, // ????
20274     
20275     isFormField: true, // not sure where this is needed!!!!
20276
20277     onResize : function(){
20278         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20279         if(!this.boxLabel){
20280             this.el.alignTo(this.wrap, 'c-c');
20281         }
20282     },
20283
20284     initEvents : function(){
20285         Roo.form.Checkbox.superclass.initEvents.call(this);
20286         this.el.on("click", this.onClick,  this);
20287         this.el.on("change", this.onClick,  this);
20288     },
20289
20290
20291     getResizeEl : function(){
20292         return this.wrap;
20293     },
20294
20295     getPositionEl : function(){
20296         return this.wrap;
20297     },
20298
20299     // private
20300     onRender : function(ct, position){
20301         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20302         /*
20303         if(this.inputValue !== undefined){
20304             this.el.dom.value = this.inputValue;
20305         }
20306         */
20307         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20308         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20309         var viewEl = this.wrap.createChild({ 
20310             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20311         this.viewEl = viewEl;   
20312         this.wrap.on('click', this.onClick,  this); 
20313         
20314         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20315         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20316         
20317         
20318         
20319         if(this.boxLabel){
20320             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20321         //    viewEl.on('click', this.onClick,  this); 
20322         }
20323         //if(this.checked){
20324             this.setChecked(this.checked);
20325         //}else{
20326             //this.checked = this.el.dom;
20327         //}
20328
20329     },
20330
20331     // private
20332     initValue : Roo.emptyFn,
20333
20334     /**
20335      * Returns the checked state of the checkbox.
20336      * @return {Boolean} True if checked, else false
20337      */
20338     getValue : function(){
20339         if(this.el){
20340             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20341         }
20342         return this.valueOff;
20343         
20344     },
20345
20346         // private
20347     onClick : function(){ 
20348         if (this.disabled) {
20349             return;
20350         }
20351         this.setChecked(!this.checked);
20352
20353         //if(this.el.dom.checked != this.checked){
20354         //    this.setValue(this.el.dom.checked);
20355        // }
20356     },
20357
20358     /**
20359      * Sets the checked state of the checkbox.
20360      * On is always based on a string comparison between inputValue and the param.
20361      * @param {Boolean/String} value - the value to set 
20362      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20363      */
20364     setValue : function(v,suppressEvent){
20365         
20366         
20367         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20368         //if(this.el && this.el.dom){
20369         //    this.el.dom.checked = this.checked;
20370         //    this.el.dom.defaultChecked = this.checked;
20371         //}
20372         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20373         //this.fireEvent("check", this, this.checked);
20374     },
20375     // private..
20376     setChecked : function(state,suppressEvent)
20377     {
20378         if (this.inSetChecked) {
20379             this.checked = state;
20380             return;
20381         }
20382         
20383     
20384         if(this.wrap){
20385             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20386         }
20387         this.checked = state;
20388         if(suppressEvent !== true){
20389             this.fireEvent('check', this, state);
20390         }
20391         this.inSetChecked = true;
20392         this.el.dom.value = state ? this.inputValue : this.valueOff;
20393         this.inSetChecked = false;
20394         
20395     },
20396     // handle setting of hidden value by some other method!!?!?
20397     setFromHidden: function()
20398     {
20399         if(!this.el){
20400             return;
20401         }
20402         //console.log("SET FROM HIDDEN");
20403         //alert('setFrom hidden');
20404         this.setValue(this.el.dom.value);
20405     },
20406     
20407     onDestroy : function()
20408     {
20409         if(this.viewEl){
20410             Roo.get(this.viewEl).remove();
20411         }
20412          
20413         Roo.form.Checkbox.superclass.onDestroy.call(this);
20414     },
20415     
20416     setBoxLabel : function(str)
20417     {
20418         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20419     }
20420
20421 });/*
20422  * Based on:
20423  * Ext JS Library 1.1.1
20424  * Copyright(c) 2006-2007, Ext JS, LLC.
20425  *
20426  * Originally Released Under LGPL - original licence link has changed is not relivant.
20427  *
20428  * Fork - LGPL
20429  * <script type="text/javascript">
20430  */
20431  
20432 /**
20433  * @class Roo.form.Radio
20434  * @extends Roo.form.Checkbox
20435  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20436  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20437  * @constructor
20438  * Creates a new Radio
20439  * @param {Object} config Configuration options
20440  */
20441 Roo.form.Radio = function(){
20442     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20443 };
20444 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20445     inputType: 'radio',
20446
20447     /**
20448      * If this radio is part of a group, it will return the selected value
20449      * @return {String}
20450      */
20451     getGroupValue : function(){
20452         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20453     },
20454     
20455     
20456     onRender : function(ct, position){
20457         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20458         
20459         if(this.inputValue !== undefined){
20460             this.el.dom.value = this.inputValue;
20461         }
20462          
20463         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20464         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20465         //var viewEl = this.wrap.createChild({ 
20466         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20467         //this.viewEl = viewEl;   
20468         //this.wrap.on('click', this.onClick,  this); 
20469         
20470         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20471         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20472         
20473         
20474         
20475         if(this.boxLabel){
20476             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20477         //    viewEl.on('click', this.onClick,  this); 
20478         }
20479          if(this.checked){
20480             this.el.dom.checked =   'checked' ;
20481         }
20482          
20483     } 
20484     
20485     
20486 });//<script type="text/javascript">
20487
20488 /*
20489  * Based  Ext JS Library 1.1.1
20490  * Copyright(c) 2006-2007, Ext JS, LLC.
20491  * LGPL
20492  *
20493  */
20494  
20495 /**
20496  * @class Roo.HtmlEditorCore
20497  * @extends Roo.Component
20498  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20499  *
20500  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20501  */
20502
20503 Roo.HtmlEditorCore = function(config){
20504     
20505     
20506     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20507     
20508     
20509     this.addEvents({
20510         /**
20511          * @event initialize
20512          * Fires when the editor is fully initialized (including the iframe)
20513          * @param {Roo.HtmlEditorCore} this
20514          */
20515         initialize: true,
20516         /**
20517          * @event activate
20518          * Fires when the editor is first receives the focus. Any insertion must wait
20519          * until after this event.
20520          * @param {Roo.HtmlEditorCore} this
20521          */
20522         activate: true,
20523          /**
20524          * @event beforesync
20525          * Fires before the textarea is updated with content from the editor iframe. Return false
20526          * to cancel the sync.
20527          * @param {Roo.HtmlEditorCore} this
20528          * @param {String} html
20529          */
20530         beforesync: true,
20531          /**
20532          * @event beforepush
20533          * Fires before the iframe editor is updated with content from the textarea. Return false
20534          * to cancel the push.
20535          * @param {Roo.HtmlEditorCore} this
20536          * @param {String} html
20537          */
20538         beforepush: true,
20539          /**
20540          * @event sync
20541          * Fires when the textarea is updated with content from the editor iframe.
20542          * @param {Roo.HtmlEditorCore} this
20543          * @param {String} html
20544          */
20545         sync: true,
20546          /**
20547          * @event push
20548          * Fires when the iframe editor is updated with content from the textarea.
20549          * @param {Roo.HtmlEditorCore} this
20550          * @param {String} html
20551          */
20552         push: true,
20553         
20554         /**
20555          * @event editorevent
20556          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20557          * @param {Roo.HtmlEditorCore} this
20558          */
20559         editorevent: true
20560         
20561     });
20562     
20563     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20564     
20565     // defaults : white / black...
20566     this.applyBlacklists();
20567     
20568     
20569     
20570 };
20571
20572
20573 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20574
20575
20576      /**
20577      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20578      */
20579     
20580     owner : false,
20581     
20582      /**
20583      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20584      *                        Roo.resizable.
20585      */
20586     resizable : false,
20587      /**
20588      * @cfg {Number} height (in pixels)
20589      */   
20590     height: 300,
20591    /**
20592      * @cfg {Number} width (in pixels)
20593      */   
20594     width: 500,
20595     
20596     /**
20597      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20598      * 
20599      */
20600     stylesheets: false,
20601     
20602     // id of frame..
20603     frameId: false,
20604     
20605     // private properties
20606     validationEvent : false,
20607     deferHeight: true,
20608     initialized : false,
20609     activated : false,
20610     sourceEditMode : false,
20611     onFocus : Roo.emptyFn,
20612     iframePad:3,
20613     hideMode:'offsets',
20614     
20615     clearUp: true,
20616     
20617     // blacklist + whitelisted elements..
20618     black: false,
20619     white: false,
20620      
20621     bodyCls : '',
20622
20623     /**
20624      * Protected method that will not generally be called directly. It
20625      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20626      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20627      */
20628     getDocMarkup : function(){
20629         // body styles..
20630         var st = '';
20631         
20632         // inherit styels from page...?? 
20633         if (this.stylesheets === false) {
20634             
20635             Roo.get(document.head).select('style').each(function(node) {
20636                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20637             });
20638             
20639             Roo.get(document.head).select('link').each(function(node) { 
20640                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20641             });
20642             
20643         } else if (!this.stylesheets.length) {
20644                 // simple..
20645                 st = '<style type="text/css">' +
20646                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20647                    '</style>';
20648         } else { 
20649             st = '<style type="text/css">' +
20650                     this.stylesheets +
20651                 '</style>';
20652         }
20653         
20654         st +=  '<style type="text/css">' +
20655             'IMG { cursor: pointer } ' +
20656         '</style>';
20657
20658         var cls = 'roo-htmleditor-body';
20659         
20660         if(this.bodyCls.length){
20661             cls += ' ' + this.bodyCls;
20662         }
20663         
20664         return '<html><head>' + st  +
20665             //<style type="text/css">' +
20666             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20667             //'</style>' +
20668             ' </head><body class="' +  cls + '"></body></html>';
20669     },
20670
20671     // private
20672     onRender : function(ct, position)
20673     {
20674         var _t = this;
20675         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20676         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20677         
20678         
20679         this.el.dom.style.border = '0 none';
20680         this.el.dom.setAttribute('tabIndex', -1);
20681         this.el.addClass('x-hidden hide');
20682         
20683         
20684         
20685         if(Roo.isIE){ // fix IE 1px bogus margin
20686             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20687         }
20688        
20689         
20690         this.frameId = Roo.id();
20691         
20692          
20693         
20694         var iframe = this.owner.wrap.createChild({
20695             tag: 'iframe',
20696             cls: 'form-control', // bootstrap..
20697             id: this.frameId,
20698             name: this.frameId,
20699             frameBorder : 'no',
20700             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20701         }, this.el
20702         );
20703         
20704         
20705         this.iframe = iframe.dom;
20706
20707          this.assignDocWin();
20708         
20709         this.doc.designMode = 'on';
20710        
20711         this.doc.open();
20712         this.doc.write(this.getDocMarkup());
20713         this.doc.close();
20714
20715         
20716         var task = { // must defer to wait for browser to be ready
20717             run : function(){
20718                 //console.log("run task?" + this.doc.readyState);
20719                 this.assignDocWin();
20720                 if(this.doc.body || this.doc.readyState == 'complete'){
20721                     try {
20722                         this.doc.designMode="on";
20723                     } catch (e) {
20724                         return;
20725                     }
20726                     Roo.TaskMgr.stop(task);
20727                     this.initEditor.defer(10, this);
20728                 }
20729             },
20730             interval : 10,
20731             duration: 10000,
20732             scope: this
20733         };
20734         Roo.TaskMgr.start(task);
20735
20736     },
20737
20738     // private
20739     onResize : function(w, h)
20740     {
20741          Roo.log('resize: ' +w + ',' + h );
20742         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20743         if(!this.iframe){
20744             return;
20745         }
20746         if(typeof w == 'number'){
20747             
20748             this.iframe.style.width = w + 'px';
20749         }
20750         if(typeof h == 'number'){
20751             
20752             this.iframe.style.height = h + 'px';
20753             if(this.doc){
20754                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20755             }
20756         }
20757         
20758     },
20759
20760     /**
20761      * Toggles the editor between standard and source edit mode.
20762      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20763      */
20764     toggleSourceEdit : function(sourceEditMode){
20765         
20766         this.sourceEditMode = sourceEditMode === true;
20767         
20768         if(this.sourceEditMode){
20769  
20770             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20771             
20772         }else{
20773             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20774             //this.iframe.className = '';
20775             this.deferFocus();
20776         }
20777         //this.setSize(this.owner.wrap.getSize());
20778         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20779     },
20780
20781     
20782   
20783
20784     /**
20785      * Protected method that will not generally be called directly. If you need/want
20786      * custom HTML cleanup, this is the method you should override.
20787      * @param {String} html The HTML to be cleaned
20788      * return {String} The cleaned HTML
20789      */
20790     cleanHtml : function(html){
20791         html = String(html);
20792         if(html.length > 5){
20793             if(Roo.isSafari){ // strip safari nonsense
20794                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20795             }
20796         }
20797         if(html == '&nbsp;'){
20798             html = '';
20799         }
20800         return html;
20801     },
20802
20803     /**
20804      * HTML Editor -> Textarea
20805      * Protected method that will not generally be called directly. Syncs the contents
20806      * of the editor iframe with the textarea.
20807      */
20808     syncValue : function(){
20809         if(this.initialized){
20810             var bd = (this.doc.body || this.doc.documentElement);
20811             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20812             var html = bd.innerHTML;
20813             if(Roo.isSafari){
20814                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20815                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20816                 if(m && m[1]){
20817                     html = '<div style="'+m[0]+'">' + html + '</div>';
20818                 }
20819             }
20820             html = this.cleanHtml(html);
20821             // fix up the special chars.. normaly like back quotes in word...
20822             // however we do not want to do this with chinese..
20823             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20824                 
20825                 var cc = match.charCodeAt();
20826
20827                 // Get the character value, handling surrogate pairs
20828                 if (match.length == 2) {
20829                     // It's a surrogate pair, calculate the Unicode code point
20830                     var high = match.charCodeAt(0) - 0xD800;
20831                     var low  = match.charCodeAt(1) - 0xDC00;
20832                     cc = (high * 0x400) + low + 0x10000;
20833                 }  else if (
20834                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20835                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20836                     (cc >= 0xf900 && cc < 0xfb00 )
20837                 ) {
20838                         return match;
20839                 }  
20840          
20841                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20842                 return "&#" + cc + ";";
20843                 
20844                 
20845             });
20846             
20847             
20848              
20849             if(this.owner.fireEvent('beforesync', this, html) !== false){
20850                 this.el.dom.value = html;
20851                 this.owner.fireEvent('sync', this, html);
20852             }
20853         }
20854     },
20855
20856     /**
20857      * Protected method that will not generally be called directly. Pushes the value of the textarea
20858      * into the iframe editor.
20859      */
20860     pushValue : function(){
20861         if(this.initialized){
20862             var v = this.el.dom.value.trim();
20863             
20864 //            if(v.length < 1){
20865 //                v = '&#160;';
20866 //            }
20867             
20868             if(this.owner.fireEvent('beforepush', this, v) !== false){
20869                 var d = (this.doc.body || this.doc.documentElement);
20870                 d.innerHTML = v;
20871                 this.cleanUpPaste();
20872                 this.el.dom.value = d.innerHTML;
20873                 this.owner.fireEvent('push', this, v);
20874             }
20875         }
20876     },
20877
20878     // private
20879     deferFocus : function(){
20880         this.focus.defer(10, this);
20881     },
20882
20883     // doc'ed in Field
20884     focus : function(){
20885         if(this.win && !this.sourceEditMode){
20886             this.win.focus();
20887         }else{
20888             this.el.focus();
20889         }
20890     },
20891     
20892     assignDocWin: function()
20893     {
20894         var iframe = this.iframe;
20895         
20896          if(Roo.isIE){
20897             this.doc = iframe.contentWindow.document;
20898             this.win = iframe.contentWindow;
20899         } else {
20900 //            if (!Roo.get(this.frameId)) {
20901 //                return;
20902 //            }
20903 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20904 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20905             
20906             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20907                 return;
20908             }
20909             
20910             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20911             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20912         }
20913     },
20914     
20915     // private
20916     initEditor : function(){
20917         //console.log("INIT EDITOR");
20918         this.assignDocWin();
20919         
20920         
20921         
20922         this.doc.designMode="on";
20923         this.doc.open();
20924         this.doc.write(this.getDocMarkup());
20925         this.doc.close();
20926         
20927         var dbody = (this.doc.body || this.doc.documentElement);
20928         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20929         // this copies styles from the containing element into thsi one..
20930         // not sure why we need all of this..
20931         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20932         
20933         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20934         //ss['background-attachment'] = 'fixed'; // w3c
20935         dbody.bgProperties = 'fixed'; // ie
20936         //Roo.DomHelper.applyStyles(dbody, ss);
20937         Roo.EventManager.on(this.doc, {
20938             //'mousedown': this.onEditorEvent,
20939             'mouseup': this.onEditorEvent,
20940             'dblclick': this.onEditorEvent,
20941             'click': this.onEditorEvent,
20942             'keyup': this.onEditorEvent,
20943             buffer:100,
20944             scope: this
20945         });
20946         if(Roo.isGecko){
20947             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20948         }
20949         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20950             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20951         }
20952         this.initialized = true;
20953
20954         this.owner.fireEvent('initialize', this);
20955         this.pushValue();
20956     },
20957
20958     // private
20959     onDestroy : function(){
20960         
20961         
20962         
20963         if(this.rendered){
20964             
20965             //for (var i =0; i < this.toolbars.length;i++) {
20966             //    // fixme - ask toolbars for heights?
20967             //    this.toolbars[i].onDestroy();
20968            // }
20969             
20970             //this.wrap.dom.innerHTML = '';
20971             //this.wrap.remove();
20972         }
20973     },
20974
20975     // private
20976     onFirstFocus : function(){
20977         
20978         this.assignDocWin();
20979         
20980         
20981         this.activated = true;
20982          
20983     
20984         if(Roo.isGecko){ // prevent silly gecko errors
20985             this.win.focus();
20986             var s = this.win.getSelection();
20987             if(!s.focusNode || s.focusNode.nodeType != 3){
20988                 var r = s.getRangeAt(0);
20989                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20990                 r.collapse(true);
20991                 this.deferFocus();
20992             }
20993             try{
20994                 this.execCmd('useCSS', true);
20995                 this.execCmd('styleWithCSS', false);
20996             }catch(e){}
20997         }
20998         this.owner.fireEvent('activate', this);
20999     },
21000
21001     // private
21002     adjustFont: function(btn){
21003         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21004         //if(Roo.isSafari){ // safari
21005         //    adjust *= 2;
21006        // }
21007         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21008         if(Roo.isSafari){ // safari
21009             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21010             v =  (v < 10) ? 10 : v;
21011             v =  (v > 48) ? 48 : v;
21012             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21013             
21014         }
21015         
21016         
21017         v = Math.max(1, v+adjust);
21018         
21019         this.execCmd('FontSize', v  );
21020     },
21021
21022     onEditorEvent : function(e)
21023     {
21024         this.owner.fireEvent('editorevent', this, e);
21025       //  this.updateToolbar();
21026         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21027     },
21028
21029     insertTag : function(tg)
21030     {
21031         // could be a bit smarter... -> wrap the current selected tRoo..
21032         if (tg.toLowerCase() == 'span' ||
21033             tg.toLowerCase() == 'code' ||
21034             tg.toLowerCase() == 'sup' ||
21035             tg.toLowerCase() == 'sub' 
21036             ) {
21037             
21038             range = this.createRange(this.getSelection());
21039             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21040             wrappingNode.appendChild(range.extractContents());
21041             range.insertNode(wrappingNode);
21042
21043             return;
21044             
21045             
21046             
21047         }
21048         this.execCmd("formatblock",   tg);
21049         
21050     },
21051     
21052     insertText : function(txt)
21053     {
21054         
21055         
21056         var range = this.createRange();
21057         range.deleteContents();
21058                //alert(Sender.getAttribute('label'));
21059                
21060         range.insertNode(this.doc.createTextNode(txt));
21061     } ,
21062     
21063      
21064
21065     /**
21066      * Executes a Midas editor command on the editor document and performs necessary focus and
21067      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21068      * @param {String} cmd The Midas command
21069      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21070      */
21071     relayCmd : function(cmd, value){
21072         this.win.focus();
21073         this.execCmd(cmd, value);
21074         this.owner.fireEvent('editorevent', this);
21075         //this.updateToolbar();
21076         this.owner.deferFocus();
21077     },
21078
21079     /**
21080      * Executes a Midas editor command directly on the editor document.
21081      * For visual commands, you should use {@link #relayCmd} instead.
21082      * <b>This should only be called after the editor is initialized.</b>
21083      * @param {String} cmd The Midas command
21084      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21085      */
21086     execCmd : function(cmd, value){
21087         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21088         this.syncValue();
21089     },
21090  
21091  
21092    
21093     /**
21094      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21095      * to insert tRoo.
21096      * @param {String} text | dom node.. 
21097      */
21098     insertAtCursor : function(text)
21099     {
21100         
21101         if(!this.activated){
21102             return;
21103         }
21104         /*
21105         if(Roo.isIE){
21106             this.win.focus();
21107             var r = this.doc.selection.createRange();
21108             if(r){
21109                 r.collapse(true);
21110                 r.pasteHTML(text);
21111                 this.syncValue();
21112                 this.deferFocus();
21113             
21114             }
21115             return;
21116         }
21117         */
21118         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21119             this.win.focus();
21120             
21121             
21122             // from jquery ui (MIT licenced)
21123             var range, node;
21124             var win = this.win;
21125             
21126             if (win.getSelection && win.getSelection().getRangeAt) {
21127                 range = win.getSelection().getRangeAt(0);
21128                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21129                 range.insertNode(node);
21130             } else if (win.document.selection && win.document.selection.createRange) {
21131                 // no firefox support
21132                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21133                 win.document.selection.createRange().pasteHTML(txt);
21134             } else {
21135                 // no firefox support
21136                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21137                 this.execCmd('InsertHTML', txt);
21138             } 
21139             
21140             this.syncValue();
21141             
21142             this.deferFocus();
21143         }
21144     },
21145  // private
21146     mozKeyPress : function(e){
21147         if(e.ctrlKey){
21148             var c = e.getCharCode(), cmd;
21149           
21150             if(c > 0){
21151                 c = String.fromCharCode(c).toLowerCase();
21152                 switch(c){
21153                     case 'b':
21154                         cmd = 'bold';
21155                         break;
21156                     case 'i':
21157                         cmd = 'italic';
21158                         break;
21159                     
21160                     case 'u':
21161                         cmd = 'underline';
21162                         break;
21163                     
21164                     case 'v':
21165                         this.cleanUpPaste.defer(100, this);
21166                         return;
21167                         
21168                 }
21169                 if(cmd){
21170                     this.win.focus();
21171                     this.execCmd(cmd);
21172                     this.deferFocus();
21173                     e.preventDefault();
21174                 }
21175                 
21176             }
21177         }
21178     },
21179
21180     // private
21181     fixKeys : function(){ // load time branching for fastest keydown performance
21182         if(Roo.isIE){
21183             return function(e){
21184                 var k = e.getKey(), r;
21185                 if(k == e.TAB){
21186                     e.stopEvent();
21187                     r = this.doc.selection.createRange();
21188                     if(r){
21189                         r.collapse(true);
21190                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21191                         this.deferFocus();
21192                     }
21193                     return;
21194                 }
21195                 
21196                 if(k == e.ENTER){
21197                     r = this.doc.selection.createRange();
21198                     if(r){
21199                         var target = r.parentElement();
21200                         if(!target || target.tagName.toLowerCase() != 'li'){
21201                             e.stopEvent();
21202                             r.pasteHTML('<br />');
21203                             r.collapse(false);
21204                             r.select();
21205                         }
21206                     }
21207                 }
21208                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21209                     this.cleanUpPaste.defer(100, this);
21210                     return;
21211                 }
21212                 
21213                 
21214             };
21215         }else if(Roo.isOpera){
21216             return function(e){
21217                 var k = e.getKey();
21218                 if(k == e.TAB){
21219                     e.stopEvent();
21220                     this.win.focus();
21221                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21222                     this.deferFocus();
21223                 }
21224                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21225                     this.cleanUpPaste.defer(100, this);
21226                     return;
21227                 }
21228                 
21229             };
21230         }else if(Roo.isSafari){
21231             return function(e){
21232                 var k = e.getKey();
21233                 
21234                 if(k == e.TAB){
21235                     e.stopEvent();
21236                     this.execCmd('InsertText','\t');
21237                     this.deferFocus();
21238                     return;
21239                 }
21240                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21241                     this.cleanUpPaste.defer(100, this);
21242                     return;
21243                 }
21244                 
21245              };
21246         }
21247     }(),
21248     
21249     getAllAncestors: function()
21250     {
21251         var p = this.getSelectedNode();
21252         var a = [];
21253         if (!p) {
21254             a.push(p); // push blank onto stack..
21255             p = this.getParentElement();
21256         }
21257         
21258         
21259         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21260             a.push(p);
21261             p = p.parentNode;
21262         }
21263         a.push(this.doc.body);
21264         return a;
21265     },
21266     lastSel : false,
21267     lastSelNode : false,
21268     
21269     
21270     getSelection : function() 
21271     {
21272         this.assignDocWin();
21273         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21274     },
21275     
21276     getSelectedNode: function() 
21277     {
21278         // this may only work on Gecko!!!
21279         
21280         // should we cache this!!!!
21281         
21282         
21283         
21284          
21285         var range = this.createRange(this.getSelection()).cloneRange();
21286         
21287         if (Roo.isIE) {
21288             var parent = range.parentElement();
21289             while (true) {
21290                 var testRange = range.duplicate();
21291                 testRange.moveToElementText(parent);
21292                 if (testRange.inRange(range)) {
21293                     break;
21294                 }
21295                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21296                     break;
21297                 }
21298                 parent = parent.parentElement;
21299             }
21300             return parent;
21301         }
21302         
21303         // is ancestor a text element.
21304         var ac =  range.commonAncestorContainer;
21305         if (ac.nodeType == 3) {
21306             ac = ac.parentNode;
21307         }
21308         
21309         var ar = ac.childNodes;
21310          
21311         var nodes = [];
21312         var other_nodes = [];
21313         var has_other_nodes = false;
21314         for (var i=0;i<ar.length;i++) {
21315             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21316                 continue;
21317             }
21318             // fullly contained node.
21319             
21320             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21321                 nodes.push(ar[i]);
21322                 continue;
21323             }
21324             
21325             // probably selected..
21326             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21327                 other_nodes.push(ar[i]);
21328                 continue;
21329             }
21330             // outer..
21331             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21332                 continue;
21333             }
21334             
21335             
21336             has_other_nodes = true;
21337         }
21338         if (!nodes.length && other_nodes.length) {
21339             nodes= other_nodes;
21340         }
21341         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21342             return false;
21343         }
21344         
21345         return nodes[0];
21346     },
21347     createRange: function(sel)
21348     {
21349         // this has strange effects when using with 
21350         // top toolbar - not sure if it's a great idea.
21351         //this.editor.contentWindow.focus();
21352         if (typeof sel != "undefined") {
21353             try {
21354                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21355             } catch(e) {
21356                 return this.doc.createRange();
21357             }
21358         } else {
21359             return this.doc.createRange();
21360         }
21361     },
21362     getParentElement: function()
21363     {
21364         
21365         this.assignDocWin();
21366         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21367         
21368         var range = this.createRange(sel);
21369          
21370         try {
21371             var p = range.commonAncestorContainer;
21372             while (p.nodeType == 3) { // text node
21373                 p = p.parentNode;
21374             }
21375             return p;
21376         } catch (e) {
21377             return null;
21378         }
21379     
21380     },
21381     /***
21382      *
21383      * Range intersection.. the hard stuff...
21384      *  '-1' = before
21385      *  '0' = hits..
21386      *  '1' = after.
21387      *         [ -- selected range --- ]
21388      *   [fail]                        [fail]
21389      *
21390      *    basically..
21391      *      if end is before start or  hits it. fail.
21392      *      if start is after end or hits it fail.
21393      *
21394      *   if either hits (but other is outside. - then it's not 
21395      *   
21396      *    
21397      **/
21398     
21399     
21400     // @see http://www.thismuchiknow.co.uk/?p=64.
21401     rangeIntersectsNode : function(range, node)
21402     {
21403         var nodeRange = node.ownerDocument.createRange();
21404         try {
21405             nodeRange.selectNode(node);
21406         } catch (e) {
21407             nodeRange.selectNodeContents(node);
21408         }
21409     
21410         var rangeStartRange = range.cloneRange();
21411         rangeStartRange.collapse(true);
21412     
21413         var rangeEndRange = range.cloneRange();
21414         rangeEndRange.collapse(false);
21415     
21416         var nodeStartRange = nodeRange.cloneRange();
21417         nodeStartRange.collapse(true);
21418     
21419         var nodeEndRange = nodeRange.cloneRange();
21420         nodeEndRange.collapse(false);
21421     
21422         return rangeStartRange.compareBoundaryPoints(
21423                  Range.START_TO_START, nodeEndRange) == -1 &&
21424                rangeEndRange.compareBoundaryPoints(
21425                  Range.START_TO_START, nodeStartRange) == 1;
21426         
21427          
21428     },
21429     rangeCompareNode : function(range, node)
21430     {
21431         var nodeRange = node.ownerDocument.createRange();
21432         try {
21433             nodeRange.selectNode(node);
21434         } catch (e) {
21435             nodeRange.selectNodeContents(node);
21436         }
21437         
21438         
21439         range.collapse(true);
21440     
21441         nodeRange.collapse(true);
21442      
21443         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21444         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21445          
21446         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21447         
21448         var nodeIsBefore   =  ss == 1;
21449         var nodeIsAfter    = ee == -1;
21450         
21451         if (nodeIsBefore && nodeIsAfter) {
21452             return 0; // outer
21453         }
21454         if (!nodeIsBefore && nodeIsAfter) {
21455             return 1; //right trailed.
21456         }
21457         
21458         if (nodeIsBefore && !nodeIsAfter) {
21459             return 2;  // left trailed.
21460         }
21461         // fully contined.
21462         return 3;
21463     },
21464
21465     // private? - in a new class?
21466     cleanUpPaste :  function()
21467     {
21468         // cleans up the whole document..
21469         Roo.log('cleanuppaste');
21470         
21471         this.cleanUpChildren(this.doc.body);
21472         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21473         if (clean != this.doc.body.innerHTML) {
21474             this.doc.body.innerHTML = clean;
21475         }
21476         
21477     },
21478     
21479     cleanWordChars : function(input) {// change the chars to hex code
21480         var he = Roo.HtmlEditorCore;
21481         
21482         var output = input;
21483         Roo.each(he.swapCodes, function(sw) { 
21484             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21485             
21486             output = output.replace(swapper, sw[1]);
21487         });
21488         
21489         return output;
21490     },
21491     
21492     
21493     cleanUpChildren : function (n)
21494     {
21495         if (!n.childNodes.length) {
21496             return;
21497         }
21498         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21499            this.cleanUpChild(n.childNodes[i]);
21500         }
21501     },
21502     
21503     
21504         
21505     
21506     cleanUpChild : function (node)
21507     {
21508         var ed = this;
21509         //console.log(node);
21510         if (node.nodeName == "#text") {
21511             // clean up silly Windows -- stuff?
21512             return; 
21513         }
21514         if (node.nodeName == "#comment") {
21515             node.parentNode.removeChild(node);
21516             // clean up silly Windows -- stuff?
21517             return; 
21518         }
21519         var lcname = node.tagName.toLowerCase();
21520         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21521         // whitelist of tags..
21522         
21523         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21524             // remove node.
21525             node.parentNode.removeChild(node);
21526             return;
21527             
21528         }
21529         
21530         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21531         
21532         // spans with no attributes - just remove them..
21533         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21534             remove_keep_children = true;
21535         }
21536         
21537         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21538         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21539         
21540         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21541         //    remove_keep_children = true;
21542         //}
21543         
21544         if (remove_keep_children) {
21545             this.cleanUpChildren(node);
21546             // inserts everything just before this node...
21547             while (node.childNodes.length) {
21548                 var cn = node.childNodes[0];
21549                 node.removeChild(cn);
21550                 node.parentNode.insertBefore(cn, node);
21551             }
21552             node.parentNode.removeChild(node);
21553             return;
21554         }
21555         
21556         if (!node.attributes || !node.attributes.length) {
21557             
21558           
21559             
21560             
21561             this.cleanUpChildren(node);
21562             return;
21563         }
21564         
21565         function cleanAttr(n,v)
21566         {
21567             
21568             if (v.match(/^\./) || v.match(/^\//)) {
21569                 return;
21570             }
21571             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21572                 return;
21573             }
21574             if (v.match(/^#/)) {
21575                 return;
21576             }
21577 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21578             node.removeAttribute(n);
21579             
21580         }
21581         
21582         var cwhite = this.cwhite;
21583         var cblack = this.cblack;
21584             
21585         function cleanStyle(n,v)
21586         {
21587             if (v.match(/expression/)) { //XSS?? should we even bother..
21588                 node.removeAttribute(n);
21589                 return;
21590             }
21591             
21592             var parts = v.split(/;/);
21593             var clean = [];
21594             
21595             Roo.each(parts, function(p) {
21596                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21597                 if (!p.length) {
21598                     return true;
21599                 }
21600                 var l = p.split(':').shift().replace(/\s+/g,'');
21601                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21602                 
21603                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21604 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21605                     //node.removeAttribute(n);
21606                     return true;
21607                 }
21608                 //Roo.log()
21609                 // only allow 'c whitelisted system attributes'
21610                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21611 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21612                     //node.removeAttribute(n);
21613                     return true;
21614                 }
21615                 
21616                 
21617                  
21618                 
21619                 clean.push(p);
21620                 return true;
21621             });
21622             if (clean.length) { 
21623                 node.setAttribute(n, clean.join(';'));
21624             } else {
21625                 node.removeAttribute(n);
21626             }
21627             
21628         }
21629         
21630         
21631         for (var i = node.attributes.length-1; i > -1 ; i--) {
21632             var a = node.attributes[i];
21633             //console.log(a);
21634             
21635             if (a.name.toLowerCase().substr(0,2)=='on')  {
21636                 node.removeAttribute(a.name);
21637                 continue;
21638             }
21639             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21640                 node.removeAttribute(a.name);
21641                 continue;
21642             }
21643             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21644                 cleanAttr(a.name,a.value); // fixme..
21645                 continue;
21646             }
21647             if (a.name == 'style') {
21648                 cleanStyle(a.name,a.value);
21649                 continue;
21650             }
21651             /// clean up MS crap..
21652             // tecnically this should be a list of valid class'es..
21653             
21654             
21655             if (a.name == 'class') {
21656                 if (a.value.match(/^Mso/)) {
21657                     node.removeAttribute('class');
21658                 }
21659                 
21660                 if (a.value.match(/^body$/)) {
21661                     node.removeAttribute('class');
21662                 }
21663                 continue;
21664             }
21665             
21666             // style cleanup!?
21667             // class cleanup?
21668             
21669         }
21670         
21671         
21672         this.cleanUpChildren(node);
21673         
21674         
21675     },
21676     
21677     /**
21678      * Clean up MS wordisms...
21679      */
21680     cleanWord : function(node)
21681     {
21682         if (!node) {
21683             this.cleanWord(this.doc.body);
21684             return;
21685         }
21686         
21687         if(
21688                 node.nodeName == 'SPAN' &&
21689                 !node.hasAttributes() &&
21690                 node.childNodes.length == 1 &&
21691                 node.firstChild.nodeName == "#text"  
21692         ) {
21693             var textNode = node.firstChild;
21694             node.removeChild(textNode);
21695             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21696                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21697             }
21698             node.parentNode.insertBefore(textNode, node);
21699             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21700                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21701             }
21702             node.parentNode.removeChild(node);
21703         }
21704         
21705         if (node.nodeName == "#text") {
21706             // clean up silly Windows -- stuff?
21707             return; 
21708         }
21709         if (node.nodeName == "#comment") {
21710             node.parentNode.removeChild(node);
21711             // clean up silly Windows -- stuff?
21712             return; 
21713         }
21714         
21715         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21716             node.parentNode.removeChild(node);
21717             return;
21718         }
21719         //Roo.log(node.tagName);
21720         // remove - but keep children..
21721         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21722             //Roo.log('-- removed');
21723             while (node.childNodes.length) {
21724                 var cn = node.childNodes[0];
21725                 node.removeChild(cn);
21726                 node.parentNode.insertBefore(cn, node);
21727                 // move node to parent - and clean it..
21728                 this.cleanWord(cn);
21729             }
21730             node.parentNode.removeChild(node);
21731             /// no need to iterate chidlren = it's got none..
21732             //this.iterateChildren(node, this.cleanWord);
21733             return;
21734         }
21735         // clean styles
21736         if (node.className.length) {
21737             
21738             var cn = node.className.split(/\W+/);
21739             var cna = [];
21740             Roo.each(cn, function(cls) {
21741                 if (cls.match(/Mso[a-zA-Z]+/)) {
21742                     return;
21743                 }
21744                 cna.push(cls);
21745             });
21746             node.className = cna.length ? cna.join(' ') : '';
21747             if (!cna.length) {
21748                 node.removeAttribute("class");
21749             }
21750         }
21751         
21752         if (node.hasAttribute("lang")) {
21753             node.removeAttribute("lang");
21754         }
21755         
21756         if (node.hasAttribute("style")) {
21757             
21758             var styles = node.getAttribute("style").split(";");
21759             var nstyle = [];
21760             Roo.each(styles, function(s) {
21761                 if (!s.match(/:/)) {
21762                     return;
21763                 }
21764                 var kv = s.split(":");
21765                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21766                     return;
21767                 }
21768                 // what ever is left... we allow.
21769                 nstyle.push(s);
21770             });
21771             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21772             if (!nstyle.length) {
21773                 node.removeAttribute('style');
21774             }
21775         }
21776         this.iterateChildren(node, this.cleanWord);
21777         
21778         
21779         
21780     },
21781     /**
21782      * iterateChildren of a Node, calling fn each time, using this as the scole..
21783      * @param {DomNode} node node to iterate children of.
21784      * @param {Function} fn method of this class to call on each item.
21785      */
21786     iterateChildren : function(node, fn)
21787     {
21788         if (!node.childNodes.length) {
21789                 return;
21790         }
21791         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21792            fn.call(this, node.childNodes[i])
21793         }
21794     },
21795     
21796     
21797     /**
21798      * cleanTableWidths.
21799      *
21800      * Quite often pasting from word etc.. results in tables with column and widths.
21801      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21802      *
21803      */
21804     cleanTableWidths : function(node)
21805     {
21806          
21807          
21808         if (!node) {
21809             this.cleanTableWidths(this.doc.body);
21810             return;
21811         }
21812         
21813         // ignore list...
21814         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21815             return; 
21816         }
21817         Roo.log(node.tagName);
21818         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21819             this.iterateChildren(node, this.cleanTableWidths);
21820             return;
21821         }
21822         if (node.hasAttribute('width')) {
21823             node.removeAttribute('width');
21824         }
21825         
21826          
21827         if (node.hasAttribute("style")) {
21828             // pretty basic...
21829             
21830             var styles = node.getAttribute("style").split(";");
21831             var nstyle = [];
21832             Roo.each(styles, function(s) {
21833                 if (!s.match(/:/)) {
21834                     return;
21835                 }
21836                 var kv = s.split(":");
21837                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21838                     return;
21839                 }
21840                 // what ever is left... we allow.
21841                 nstyle.push(s);
21842             });
21843             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21844             if (!nstyle.length) {
21845                 node.removeAttribute('style');
21846             }
21847         }
21848         
21849         this.iterateChildren(node, this.cleanTableWidths);
21850         
21851         
21852     },
21853     
21854     
21855     
21856     
21857     domToHTML : function(currentElement, depth, nopadtext) {
21858         
21859         depth = depth || 0;
21860         nopadtext = nopadtext || false;
21861     
21862         if (!currentElement) {
21863             return this.domToHTML(this.doc.body);
21864         }
21865         
21866         //Roo.log(currentElement);
21867         var j;
21868         var allText = false;
21869         var nodeName = currentElement.nodeName;
21870         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21871         
21872         if  (nodeName == '#text') {
21873             
21874             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21875         }
21876         
21877         
21878         var ret = '';
21879         if (nodeName != 'BODY') {
21880              
21881             var i = 0;
21882             // Prints the node tagName, such as <A>, <IMG>, etc
21883             if (tagName) {
21884                 var attr = [];
21885                 for(i = 0; i < currentElement.attributes.length;i++) {
21886                     // quoting?
21887                     var aname = currentElement.attributes.item(i).name;
21888                     if (!currentElement.attributes.item(i).value.length) {
21889                         continue;
21890                     }
21891                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21892                 }
21893                 
21894                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21895             } 
21896             else {
21897                 
21898                 // eack
21899             }
21900         } else {
21901             tagName = false;
21902         }
21903         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21904             return ret;
21905         }
21906         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21907             nopadtext = true;
21908         }
21909         
21910         
21911         // Traverse the tree
21912         i = 0;
21913         var currentElementChild = currentElement.childNodes.item(i);
21914         var allText = true;
21915         var innerHTML  = '';
21916         lastnode = '';
21917         while (currentElementChild) {
21918             // Formatting code (indent the tree so it looks nice on the screen)
21919             var nopad = nopadtext;
21920             if (lastnode == 'SPAN') {
21921                 nopad  = true;
21922             }
21923             // text
21924             if  (currentElementChild.nodeName == '#text') {
21925                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21926                 toadd = nopadtext ? toadd : toadd.trim();
21927                 if (!nopad && toadd.length > 80) {
21928                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21929                 }
21930                 innerHTML  += toadd;
21931                 
21932                 i++;
21933                 currentElementChild = currentElement.childNodes.item(i);
21934                 lastNode = '';
21935                 continue;
21936             }
21937             allText = false;
21938             
21939             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21940                 
21941             // Recursively traverse the tree structure of the child node
21942             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21943             lastnode = currentElementChild.nodeName;
21944             i++;
21945             currentElementChild=currentElement.childNodes.item(i);
21946         }
21947         
21948         ret += innerHTML;
21949         
21950         if (!allText) {
21951                 // The remaining code is mostly for formatting the tree
21952             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21953         }
21954         
21955         
21956         if (tagName) {
21957             ret+= "</"+tagName+">";
21958         }
21959         return ret;
21960         
21961     },
21962         
21963     applyBlacklists : function()
21964     {
21965         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21966         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21967         
21968         this.white = [];
21969         this.black = [];
21970         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21971             if (b.indexOf(tag) > -1) {
21972                 return;
21973             }
21974             this.white.push(tag);
21975             
21976         }, this);
21977         
21978         Roo.each(w, function(tag) {
21979             if (b.indexOf(tag) > -1) {
21980                 return;
21981             }
21982             if (this.white.indexOf(tag) > -1) {
21983                 return;
21984             }
21985             this.white.push(tag);
21986             
21987         }, this);
21988         
21989         
21990         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21991             if (w.indexOf(tag) > -1) {
21992                 return;
21993             }
21994             this.black.push(tag);
21995             
21996         }, this);
21997         
21998         Roo.each(b, function(tag) {
21999             if (w.indexOf(tag) > -1) {
22000                 return;
22001             }
22002             if (this.black.indexOf(tag) > -1) {
22003                 return;
22004             }
22005             this.black.push(tag);
22006             
22007         }, this);
22008         
22009         
22010         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22011         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22012         
22013         this.cwhite = [];
22014         this.cblack = [];
22015         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22016             if (b.indexOf(tag) > -1) {
22017                 return;
22018             }
22019             this.cwhite.push(tag);
22020             
22021         }, this);
22022         
22023         Roo.each(w, function(tag) {
22024             if (b.indexOf(tag) > -1) {
22025                 return;
22026             }
22027             if (this.cwhite.indexOf(tag) > -1) {
22028                 return;
22029             }
22030             this.cwhite.push(tag);
22031             
22032         }, this);
22033         
22034         
22035         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22036             if (w.indexOf(tag) > -1) {
22037                 return;
22038             }
22039             this.cblack.push(tag);
22040             
22041         }, this);
22042         
22043         Roo.each(b, function(tag) {
22044             if (w.indexOf(tag) > -1) {
22045                 return;
22046             }
22047             if (this.cblack.indexOf(tag) > -1) {
22048                 return;
22049             }
22050             this.cblack.push(tag);
22051             
22052         }, this);
22053     },
22054     
22055     setStylesheets : function(stylesheets)
22056     {
22057         if(typeof(stylesheets) == 'string'){
22058             Roo.get(this.iframe.contentDocument.head).createChild({
22059                 tag : 'link',
22060                 rel : 'stylesheet',
22061                 type : 'text/css',
22062                 href : stylesheets
22063             });
22064             
22065             return;
22066         }
22067         var _this = this;
22068      
22069         Roo.each(stylesheets, function(s) {
22070             if(!s.length){
22071                 return;
22072             }
22073             
22074             Roo.get(_this.iframe.contentDocument.head).createChild({
22075                 tag : 'link',
22076                 rel : 'stylesheet',
22077                 type : 'text/css',
22078                 href : s
22079             });
22080         });
22081
22082         
22083     },
22084     
22085     removeStylesheets : function()
22086     {
22087         var _this = this;
22088         
22089         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22090             s.remove();
22091         });
22092     },
22093     
22094     setStyle : function(style)
22095     {
22096         Roo.get(this.iframe.contentDocument.head).createChild({
22097             tag : 'style',
22098             type : 'text/css',
22099             html : style
22100         });
22101
22102         return;
22103     }
22104     
22105     // hide stuff that is not compatible
22106     /**
22107      * @event blur
22108      * @hide
22109      */
22110     /**
22111      * @event change
22112      * @hide
22113      */
22114     /**
22115      * @event focus
22116      * @hide
22117      */
22118     /**
22119      * @event specialkey
22120      * @hide
22121      */
22122     /**
22123      * @cfg {String} fieldClass @hide
22124      */
22125     /**
22126      * @cfg {String} focusClass @hide
22127      */
22128     /**
22129      * @cfg {String} autoCreate @hide
22130      */
22131     /**
22132      * @cfg {String} inputType @hide
22133      */
22134     /**
22135      * @cfg {String} invalidClass @hide
22136      */
22137     /**
22138      * @cfg {String} invalidText @hide
22139      */
22140     /**
22141      * @cfg {String} msgFx @hide
22142      */
22143     /**
22144      * @cfg {String} validateOnBlur @hide
22145      */
22146 });
22147
22148 Roo.HtmlEditorCore.white = [
22149         'area', 'br', 'img', 'input', 'hr', 'wbr',
22150         
22151        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22152        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22153        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22154        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22155        'table',   'ul',         'xmp', 
22156        
22157        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22158       'thead',   'tr', 
22159      
22160       'dir', 'menu', 'ol', 'ul', 'dl',
22161        
22162       'embed',  'object'
22163 ];
22164
22165
22166 Roo.HtmlEditorCore.black = [
22167     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22168         'applet', // 
22169         'base',   'basefont', 'bgsound', 'blink',  'body', 
22170         'frame',  'frameset', 'head',    'html',   'ilayer', 
22171         'iframe', 'layer',  'link',     'meta',    'object',   
22172         'script', 'style' ,'title',  'xml' // clean later..
22173 ];
22174 Roo.HtmlEditorCore.clean = [
22175     'script', 'style', 'title', 'xml'
22176 ];
22177 Roo.HtmlEditorCore.remove = [
22178     'font'
22179 ];
22180 // attributes..
22181
22182 Roo.HtmlEditorCore.ablack = [
22183     'on'
22184 ];
22185     
22186 Roo.HtmlEditorCore.aclean = [ 
22187     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22188 ];
22189
22190 // protocols..
22191 Roo.HtmlEditorCore.pwhite= [
22192         'http',  'https',  'mailto'
22193 ];
22194
22195 // white listed style attributes.
22196 Roo.HtmlEditorCore.cwhite= [
22197       //  'text-align', /// default is to allow most things..
22198       
22199          
22200 //        'font-size'//??
22201 ];
22202
22203 // black listed style attributes.
22204 Roo.HtmlEditorCore.cblack= [
22205       //  'font-size' -- this can be set by the project 
22206 ];
22207
22208
22209 Roo.HtmlEditorCore.swapCodes   =[ 
22210     [    8211, "--" ], 
22211     [    8212, "--" ], 
22212     [    8216,  "'" ],  
22213     [    8217, "'" ],  
22214     [    8220, '"' ],  
22215     [    8221, '"' ],  
22216     [    8226, "*" ],  
22217     [    8230, "..." ]
22218 ]; 
22219
22220     //<script type="text/javascript">
22221
22222 /*
22223  * Ext JS Library 1.1.1
22224  * Copyright(c) 2006-2007, Ext JS, LLC.
22225  * Licence LGPL
22226  * 
22227  */
22228  
22229  
22230 Roo.form.HtmlEditor = function(config){
22231     
22232     
22233     
22234     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22235     
22236     if (!this.toolbars) {
22237         this.toolbars = [];
22238     }
22239     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22240     
22241     
22242 };
22243
22244 /**
22245  * @class Roo.form.HtmlEditor
22246  * @extends Roo.form.Field
22247  * Provides a lightweight HTML Editor component.
22248  *
22249  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22250  * 
22251  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22252  * supported by this editor.</b><br/><br/>
22253  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22254  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22255  */
22256 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22257     /**
22258      * @cfg {Boolean} clearUp
22259      */
22260     clearUp : true,
22261       /**
22262      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22263      */
22264     toolbars : false,
22265    
22266      /**
22267      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22268      *                        Roo.resizable.
22269      */
22270     resizable : false,
22271      /**
22272      * @cfg {Number} height (in pixels)
22273      */   
22274     height: 300,
22275    /**
22276      * @cfg {Number} width (in pixels)
22277      */   
22278     width: 500,
22279     
22280     /**
22281      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22282      * 
22283      */
22284     stylesheets: false,
22285     
22286     
22287      /**
22288      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22289      * 
22290      */
22291     cblack: false,
22292     /**
22293      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22294      * 
22295      */
22296     cwhite: false,
22297     
22298      /**
22299      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22300      * 
22301      */
22302     black: false,
22303     /**
22304      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22305      * 
22306      */
22307     white: false,
22308     
22309     // id of frame..
22310     frameId: false,
22311     
22312     // private properties
22313     validationEvent : false,
22314     deferHeight: true,
22315     initialized : false,
22316     activated : false,
22317     
22318     onFocus : Roo.emptyFn,
22319     iframePad:3,
22320     hideMode:'offsets',
22321     
22322     actionMode : 'container', // defaults to hiding it...
22323     
22324     defaultAutoCreate : { // modified by initCompnoent..
22325         tag: "textarea",
22326         style:"width:500px;height:300px;",
22327         autocomplete: "new-password"
22328     },
22329
22330     // private
22331     initComponent : function(){
22332         this.addEvents({
22333             /**
22334              * @event initialize
22335              * Fires when the editor is fully initialized (including the iframe)
22336              * @param {HtmlEditor} this
22337              */
22338             initialize: true,
22339             /**
22340              * @event activate
22341              * Fires when the editor is first receives the focus. Any insertion must wait
22342              * until after this event.
22343              * @param {HtmlEditor} this
22344              */
22345             activate: true,
22346              /**
22347              * @event beforesync
22348              * Fires before the textarea is updated with content from the editor iframe. Return false
22349              * to cancel the sync.
22350              * @param {HtmlEditor} this
22351              * @param {String} html
22352              */
22353             beforesync: true,
22354              /**
22355              * @event beforepush
22356              * Fires before the iframe editor is updated with content from the textarea. Return false
22357              * to cancel the push.
22358              * @param {HtmlEditor} this
22359              * @param {String} html
22360              */
22361             beforepush: true,
22362              /**
22363              * @event sync
22364              * Fires when the textarea is updated with content from the editor iframe.
22365              * @param {HtmlEditor} this
22366              * @param {String} html
22367              */
22368             sync: true,
22369              /**
22370              * @event push
22371              * Fires when the iframe editor is updated with content from the textarea.
22372              * @param {HtmlEditor} this
22373              * @param {String} html
22374              */
22375             push: true,
22376              /**
22377              * @event editmodechange
22378              * Fires when the editor switches edit modes
22379              * @param {HtmlEditor} this
22380              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22381              */
22382             editmodechange: true,
22383             /**
22384              * @event editorevent
22385              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22386              * @param {HtmlEditor} this
22387              */
22388             editorevent: true,
22389             /**
22390              * @event firstfocus
22391              * Fires when on first focus - needed by toolbars..
22392              * @param {HtmlEditor} this
22393              */
22394             firstfocus: true,
22395             /**
22396              * @event autosave
22397              * Auto save the htmlEditor value as a file into Events
22398              * @param {HtmlEditor} this
22399              */
22400             autosave: true,
22401             /**
22402              * @event savedpreview
22403              * preview the saved version of htmlEditor
22404              * @param {HtmlEditor} this
22405              */
22406             savedpreview: true,
22407             
22408             /**
22409             * @event stylesheetsclick
22410             * Fires when press the Sytlesheets button
22411             * @param {Roo.HtmlEditorCore} this
22412             */
22413             stylesheetsclick: true
22414         });
22415         this.defaultAutoCreate =  {
22416             tag: "textarea",
22417             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22418             autocomplete: "new-password"
22419         };
22420     },
22421
22422     /**
22423      * Protected method that will not generally be called directly. It
22424      * is called when the editor creates its toolbar. Override this method if you need to
22425      * add custom toolbar buttons.
22426      * @param {HtmlEditor} editor
22427      */
22428     createToolbar : function(editor){
22429         Roo.log("create toolbars");
22430         if (!editor.toolbars || !editor.toolbars.length) {
22431             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22432         }
22433         
22434         for (var i =0 ; i < editor.toolbars.length;i++) {
22435             editor.toolbars[i] = Roo.factory(
22436                     typeof(editor.toolbars[i]) == 'string' ?
22437                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22438                 Roo.form.HtmlEditor);
22439             editor.toolbars[i].init(editor);
22440         }
22441          
22442         
22443     },
22444
22445      
22446     // private
22447     onRender : function(ct, position)
22448     {
22449         var _t = this;
22450         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22451         
22452         this.wrap = this.el.wrap({
22453             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22454         });
22455         
22456         this.editorcore.onRender(ct, position);
22457          
22458         if (this.resizable) {
22459             this.resizeEl = new Roo.Resizable(this.wrap, {
22460                 pinned : true,
22461                 wrap: true,
22462                 dynamic : true,
22463                 minHeight : this.height,
22464                 height: this.height,
22465                 handles : this.resizable,
22466                 width: this.width,
22467                 listeners : {
22468                     resize : function(r, w, h) {
22469                         _t.onResize(w,h); // -something
22470                     }
22471                 }
22472             });
22473             
22474         }
22475         this.createToolbar(this);
22476        
22477         
22478         if(!this.width){
22479             this.setSize(this.wrap.getSize());
22480         }
22481         if (this.resizeEl) {
22482             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22483             // should trigger onReize..
22484         }
22485         
22486         this.keyNav = new Roo.KeyNav(this.el, {
22487             
22488             "tab" : function(e){
22489                 e.preventDefault();
22490                 
22491                 var value = this.getValue();
22492                 
22493                 var start = this.el.dom.selectionStart;
22494                 var end = this.el.dom.selectionEnd;
22495                 
22496                 if(!e.shiftKey){
22497                     
22498                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22499                     this.el.dom.setSelectionRange(end + 1, end + 1);
22500                     return;
22501                 }
22502                 
22503                 var f = value.substring(0, start).split("\t");
22504                 
22505                 if(f.pop().length != 0){
22506                     return;
22507                 }
22508                 
22509                 this.setValue(f.join("\t") + value.substring(end));
22510                 this.el.dom.setSelectionRange(start - 1, start - 1);
22511                 
22512             },
22513             
22514             "home" : function(e){
22515                 e.preventDefault();
22516                 
22517                 var curr = this.el.dom.selectionStart;
22518                 var lines = this.getValue().split("\n");
22519                 
22520                 if(!lines.length){
22521                     return;
22522                 }
22523                 
22524                 if(e.ctrlKey){
22525                     this.el.dom.setSelectionRange(0, 0);
22526                     return;
22527                 }
22528                 
22529                 var pos = 0;
22530                 
22531                 for (var i = 0; i < lines.length;i++) {
22532                     pos += lines[i].length;
22533                     
22534                     if(i != 0){
22535                         pos += 1;
22536                     }
22537                     
22538                     if(pos < curr){
22539                         continue;
22540                     }
22541                     
22542                     pos -= lines[i].length;
22543                     
22544                     break;
22545                 }
22546                 
22547                 if(!e.shiftKey){
22548                     this.el.dom.setSelectionRange(pos, pos);
22549                     return;
22550                 }
22551                 
22552                 this.el.dom.selectionStart = pos;
22553                 this.el.dom.selectionEnd = curr;
22554             },
22555             
22556             "end" : function(e){
22557                 e.preventDefault();
22558                 
22559                 var curr = this.el.dom.selectionStart;
22560                 var lines = this.getValue().split("\n");
22561                 
22562                 if(!lines.length){
22563                     return;
22564                 }
22565                 
22566                 if(e.ctrlKey){
22567                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22568                     return;
22569                 }
22570                 
22571                 var pos = 0;
22572                 
22573                 for (var i = 0; i < lines.length;i++) {
22574                     
22575                     pos += lines[i].length;
22576                     
22577                     if(i != 0){
22578                         pos += 1;
22579                     }
22580                     
22581                     if(pos < curr){
22582                         continue;
22583                     }
22584                     
22585                     break;
22586                 }
22587                 
22588                 if(!e.shiftKey){
22589                     this.el.dom.setSelectionRange(pos, pos);
22590                     return;
22591                 }
22592                 
22593                 this.el.dom.selectionStart = curr;
22594                 this.el.dom.selectionEnd = pos;
22595             },
22596
22597             scope : this,
22598
22599             doRelay : function(foo, bar, hname){
22600                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22601             },
22602
22603             forceKeyDown: true
22604         });
22605         
22606 //        if(this.autosave && this.w){
22607 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22608 //        }
22609     },
22610
22611     // private
22612     onResize : function(w, h)
22613     {
22614         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22615         var ew = false;
22616         var eh = false;
22617         
22618         if(this.el ){
22619             if(typeof w == 'number'){
22620                 var aw = w - this.wrap.getFrameWidth('lr');
22621                 this.el.setWidth(this.adjustWidth('textarea', aw));
22622                 ew = aw;
22623             }
22624             if(typeof h == 'number'){
22625                 var tbh = 0;
22626                 for (var i =0; i < this.toolbars.length;i++) {
22627                     // fixme - ask toolbars for heights?
22628                     tbh += this.toolbars[i].tb.el.getHeight();
22629                     if (this.toolbars[i].footer) {
22630                         tbh += this.toolbars[i].footer.el.getHeight();
22631                     }
22632                 }
22633                 
22634                 
22635                 
22636                 
22637                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22638                 ah -= 5; // knock a few pixes off for look..
22639 //                Roo.log(ah);
22640                 this.el.setHeight(this.adjustWidth('textarea', ah));
22641                 var eh = ah;
22642             }
22643         }
22644         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22645         this.editorcore.onResize(ew,eh);
22646         
22647     },
22648
22649     /**
22650      * Toggles the editor between standard and source edit mode.
22651      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22652      */
22653     toggleSourceEdit : function(sourceEditMode)
22654     {
22655         this.editorcore.toggleSourceEdit(sourceEditMode);
22656         
22657         if(this.editorcore.sourceEditMode){
22658             Roo.log('editor - showing textarea');
22659             
22660 //            Roo.log('in');
22661 //            Roo.log(this.syncValue());
22662             this.editorcore.syncValue();
22663             this.el.removeClass('x-hidden');
22664             this.el.dom.removeAttribute('tabIndex');
22665             this.el.focus();
22666             
22667             for (var i = 0; i < this.toolbars.length; i++) {
22668                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22669                     this.toolbars[i].tb.hide();
22670                     this.toolbars[i].footer.hide();
22671                 }
22672             }
22673             
22674         }else{
22675             Roo.log('editor - hiding textarea');
22676 //            Roo.log('out')
22677 //            Roo.log(this.pushValue()); 
22678             this.editorcore.pushValue();
22679             
22680             this.el.addClass('x-hidden');
22681             this.el.dom.setAttribute('tabIndex', -1);
22682             
22683             for (var i = 0; i < this.toolbars.length; i++) {
22684                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22685                     this.toolbars[i].tb.show();
22686                     this.toolbars[i].footer.show();
22687                 }
22688             }
22689             
22690             //this.deferFocus();
22691         }
22692         
22693         this.setSize(this.wrap.getSize());
22694         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22695         
22696         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22697     },
22698  
22699     // private (for BoxComponent)
22700     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22701
22702     // private (for BoxComponent)
22703     getResizeEl : function(){
22704         return this.wrap;
22705     },
22706
22707     // private (for BoxComponent)
22708     getPositionEl : function(){
22709         return this.wrap;
22710     },
22711
22712     // private
22713     initEvents : function(){
22714         this.originalValue = this.getValue();
22715     },
22716
22717     /**
22718      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22719      * @method
22720      */
22721     markInvalid : Roo.emptyFn,
22722     /**
22723      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22724      * @method
22725      */
22726     clearInvalid : Roo.emptyFn,
22727
22728     setValue : function(v){
22729         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22730         this.editorcore.pushValue();
22731     },
22732
22733      
22734     // private
22735     deferFocus : function(){
22736         this.focus.defer(10, this);
22737     },
22738
22739     // doc'ed in Field
22740     focus : function(){
22741         this.editorcore.focus();
22742         
22743     },
22744       
22745
22746     // private
22747     onDestroy : function(){
22748         
22749         
22750         
22751         if(this.rendered){
22752             
22753             for (var i =0; i < this.toolbars.length;i++) {
22754                 // fixme - ask toolbars for heights?
22755                 this.toolbars[i].onDestroy();
22756             }
22757             
22758             this.wrap.dom.innerHTML = '';
22759             this.wrap.remove();
22760         }
22761     },
22762
22763     // private
22764     onFirstFocus : function(){
22765         //Roo.log("onFirstFocus");
22766         this.editorcore.onFirstFocus();
22767          for (var i =0; i < this.toolbars.length;i++) {
22768             this.toolbars[i].onFirstFocus();
22769         }
22770         
22771     },
22772     
22773     // private
22774     syncValue : function()
22775     {
22776         this.editorcore.syncValue();
22777     },
22778     
22779     pushValue : function()
22780     {
22781         this.editorcore.pushValue();
22782     },
22783     
22784     setStylesheets : function(stylesheets)
22785     {
22786         this.editorcore.setStylesheets(stylesheets);
22787     },
22788     
22789     removeStylesheets : function()
22790     {
22791         this.editorcore.removeStylesheets();
22792     }
22793      
22794     
22795     // hide stuff that is not compatible
22796     /**
22797      * @event blur
22798      * @hide
22799      */
22800     /**
22801      * @event change
22802      * @hide
22803      */
22804     /**
22805      * @event focus
22806      * @hide
22807      */
22808     /**
22809      * @event specialkey
22810      * @hide
22811      */
22812     /**
22813      * @cfg {String} fieldClass @hide
22814      */
22815     /**
22816      * @cfg {String} focusClass @hide
22817      */
22818     /**
22819      * @cfg {String} autoCreate @hide
22820      */
22821     /**
22822      * @cfg {String} inputType @hide
22823      */
22824     /**
22825      * @cfg {String} invalidClass @hide
22826      */
22827     /**
22828      * @cfg {String} invalidText @hide
22829      */
22830     /**
22831      * @cfg {String} msgFx @hide
22832      */
22833     /**
22834      * @cfg {String} validateOnBlur @hide
22835      */
22836 });
22837  
22838     // <script type="text/javascript">
22839 /*
22840  * Based on
22841  * Ext JS Library 1.1.1
22842  * Copyright(c) 2006-2007, Ext JS, LLC.
22843  *  
22844  
22845  */
22846
22847 /**
22848  * @class Roo.form.HtmlEditorToolbar1
22849  * Basic Toolbar
22850  * 
22851  * Usage:
22852  *
22853  new Roo.form.HtmlEditor({
22854     ....
22855     toolbars : [
22856         new Roo.form.HtmlEditorToolbar1({
22857             disable : { fonts: 1 , format: 1, ..., ... , ...],
22858             btns : [ .... ]
22859         })
22860     }
22861      
22862  * 
22863  * @cfg {Object} disable List of elements to disable..
22864  * @cfg {Array} btns List of additional buttons.
22865  * 
22866  * 
22867  * NEEDS Extra CSS? 
22868  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22869  */
22870  
22871 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22872 {
22873     
22874     Roo.apply(this, config);
22875     
22876     // default disabled, based on 'good practice'..
22877     this.disable = this.disable || {};
22878     Roo.applyIf(this.disable, {
22879         fontSize : true,
22880         colors : true,
22881         specialElements : true
22882     });
22883     
22884     
22885     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22886     // dont call parent... till later.
22887 }
22888
22889 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22890     
22891     tb: false,
22892     
22893     rendered: false,
22894     
22895     editor : false,
22896     editorcore : false,
22897     /**
22898      * @cfg {Object} disable  List of toolbar elements to disable
22899          
22900      */
22901     disable : false,
22902     
22903     
22904      /**
22905      * @cfg {String} createLinkText The default text for the create link prompt
22906      */
22907     createLinkText : 'Please enter the URL for the link:',
22908     /**
22909      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22910      */
22911     defaultLinkValue : 'http:/'+'/',
22912    
22913     
22914       /**
22915      * @cfg {Array} fontFamilies An array of available font families
22916      */
22917     fontFamilies : [
22918         'Arial',
22919         'Courier New',
22920         'Tahoma',
22921         'Times New Roman',
22922         'Verdana'
22923     ],
22924     
22925     specialChars : [
22926            "&#169;",
22927           "&#174;",     
22928           "&#8482;",    
22929           "&#163;" ,    
22930          // "&#8212;",    
22931           "&#8230;",    
22932           "&#247;" ,    
22933         //  "&#225;" ,     ?? a acute?
22934            "&#8364;"    , //Euro
22935        //   "&#8220;"    ,
22936         //  "&#8221;"    ,
22937         //  "&#8226;"    ,
22938           "&#176;"  //   , // degrees
22939
22940          // "&#233;"     , // e ecute
22941          // "&#250;"     , // u ecute?
22942     ],
22943     
22944     specialElements : [
22945         {
22946             text: "Insert Table",
22947             xtype: 'MenuItem',
22948             xns : Roo.Menu,
22949             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22950                 
22951         },
22952         {    
22953             text: "Insert Image",
22954             xtype: 'MenuItem',
22955             xns : Roo.Menu,
22956             ihtml : '<img src="about:blank"/>'
22957             
22958         }
22959         
22960          
22961     ],
22962     
22963     
22964     inputElements : [ 
22965             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22966             "input:submit", "input:button", "select", "textarea", "label" ],
22967     formats : [
22968         ["p"] ,  
22969         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22970         ["pre"],[ "code"], 
22971         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22972         ['div'],['span'],
22973         ['sup'],['sub']
22974     ],
22975     
22976     cleanStyles : [
22977         "font-size"
22978     ],
22979      /**
22980      * @cfg {String} defaultFont default font to use.
22981      */
22982     defaultFont: 'tahoma',
22983    
22984     fontSelect : false,
22985     
22986     
22987     formatCombo : false,
22988     
22989     init : function(editor)
22990     {
22991         this.editor = editor;
22992         this.editorcore = editor.editorcore ? editor.editorcore : editor;
22993         var editorcore = this.editorcore;
22994         
22995         var _t = this;
22996         
22997         var fid = editorcore.frameId;
22998         var etb = this;
22999         function btn(id, toggle, handler){
23000             var xid = fid + '-'+ id ;
23001             return {
23002                 id : xid,
23003                 cmd : id,
23004                 cls : 'x-btn-icon x-edit-'+id,
23005                 enableToggle:toggle !== false,
23006                 scope: _t, // was editor...
23007                 handler:handler||_t.relayBtnCmd,
23008                 clickEvent:'mousedown',
23009                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23010                 tabIndex:-1
23011             };
23012         }
23013         
23014         
23015         
23016         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23017         this.tb = tb;
23018          // stop form submits
23019         tb.el.on('click', function(e){
23020             e.preventDefault(); // what does this do?
23021         });
23022
23023         if(!this.disable.font) { // && !Roo.isSafari){
23024             /* why no safari for fonts 
23025             editor.fontSelect = tb.el.createChild({
23026                 tag:'select',
23027                 tabIndex: -1,
23028                 cls:'x-font-select',
23029                 html: this.createFontOptions()
23030             });
23031             
23032             editor.fontSelect.on('change', function(){
23033                 var font = editor.fontSelect.dom.value;
23034                 editor.relayCmd('fontname', font);
23035                 editor.deferFocus();
23036             }, editor);
23037             
23038             tb.add(
23039                 editor.fontSelect.dom,
23040                 '-'
23041             );
23042             */
23043             
23044         };
23045         if(!this.disable.formats){
23046             this.formatCombo = new Roo.form.ComboBox({
23047                 store: new Roo.data.SimpleStore({
23048                     id : 'tag',
23049                     fields: ['tag'],
23050                     data : this.formats // from states.js
23051                 }),
23052                 blockFocus : true,
23053                 name : '',
23054                 //autoCreate : {tag: "div",  size: "20"},
23055                 displayField:'tag',
23056                 typeAhead: false,
23057                 mode: 'local',
23058                 editable : false,
23059                 triggerAction: 'all',
23060                 emptyText:'Add tag',
23061                 selectOnFocus:true,
23062                 width:135,
23063                 listeners : {
23064                     'select': function(c, r, i) {
23065                         editorcore.insertTag(r.get('tag'));
23066                         editor.focus();
23067                     }
23068                 }
23069
23070             });
23071             tb.addField(this.formatCombo);
23072             
23073         }
23074         
23075         if(!this.disable.format){
23076             tb.add(
23077                 btn('bold'),
23078                 btn('italic'),
23079                 btn('underline'),
23080                 btn('strikethrough')
23081             );
23082         };
23083         if(!this.disable.fontSize){
23084             tb.add(
23085                 '-',
23086                 
23087                 
23088                 btn('increasefontsize', false, editorcore.adjustFont),
23089                 btn('decreasefontsize', false, editorcore.adjustFont)
23090             );
23091         };
23092         
23093         
23094         if(!this.disable.colors){
23095             tb.add(
23096                 '-', {
23097                     id:editorcore.frameId +'-forecolor',
23098                     cls:'x-btn-icon x-edit-forecolor',
23099                     clickEvent:'mousedown',
23100                     tooltip: this.buttonTips['forecolor'] || undefined,
23101                     tabIndex:-1,
23102                     menu : new Roo.menu.ColorMenu({
23103                         allowReselect: true,
23104                         focus: Roo.emptyFn,
23105                         value:'000000',
23106                         plain:true,
23107                         selectHandler: function(cp, color){
23108                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23109                             editor.deferFocus();
23110                         },
23111                         scope: editorcore,
23112                         clickEvent:'mousedown'
23113                     })
23114                 }, {
23115                     id:editorcore.frameId +'backcolor',
23116                     cls:'x-btn-icon x-edit-backcolor',
23117                     clickEvent:'mousedown',
23118                     tooltip: this.buttonTips['backcolor'] || undefined,
23119                     tabIndex:-1,
23120                     menu : new Roo.menu.ColorMenu({
23121                         focus: Roo.emptyFn,
23122                         value:'FFFFFF',
23123                         plain:true,
23124                         allowReselect: true,
23125                         selectHandler: function(cp, color){
23126                             if(Roo.isGecko){
23127                                 editorcore.execCmd('useCSS', false);
23128                                 editorcore.execCmd('hilitecolor', color);
23129                                 editorcore.execCmd('useCSS', true);
23130                                 editor.deferFocus();
23131                             }else{
23132                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23133                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23134                                 editor.deferFocus();
23135                             }
23136                         },
23137                         scope:editorcore,
23138                         clickEvent:'mousedown'
23139                     })
23140                 }
23141             );
23142         };
23143         // now add all the items...
23144         
23145
23146         if(!this.disable.alignments){
23147             tb.add(
23148                 '-',
23149                 btn('justifyleft'),
23150                 btn('justifycenter'),
23151                 btn('justifyright')
23152             );
23153         };
23154
23155         //if(!Roo.isSafari){
23156             if(!this.disable.links){
23157                 tb.add(
23158                     '-',
23159                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23160                 );
23161             };
23162
23163             if(!this.disable.lists){
23164                 tb.add(
23165                     '-',
23166                     btn('insertorderedlist'),
23167                     btn('insertunorderedlist')
23168                 );
23169             }
23170             if(!this.disable.sourceEdit){
23171                 tb.add(
23172                     '-',
23173                     btn('sourceedit', true, function(btn){
23174                         this.toggleSourceEdit(btn.pressed);
23175                     })
23176                 );
23177             }
23178         //}
23179         
23180         var smenu = { };
23181         // special menu.. - needs to be tidied up..
23182         if (!this.disable.special) {
23183             smenu = {
23184                 text: "&#169;",
23185                 cls: 'x-edit-none',
23186                 
23187                 menu : {
23188                     items : []
23189                 }
23190             };
23191             for (var i =0; i < this.specialChars.length; i++) {
23192                 smenu.menu.items.push({
23193                     
23194                     html: this.specialChars[i],
23195                     handler: function(a,b) {
23196                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23197                         //editor.insertAtCursor(a.html);
23198                         
23199                     },
23200                     tabIndex:-1
23201                 });
23202             }
23203             
23204             
23205             tb.add(smenu);
23206             
23207             
23208         }
23209         
23210         var cmenu = { };
23211         if (!this.disable.cleanStyles) {
23212             cmenu = {
23213                 cls: 'x-btn-icon x-btn-clear',
23214                 
23215                 menu : {
23216                     items : []
23217                 }
23218             };
23219             for (var i =0; i < this.cleanStyles.length; i++) {
23220                 cmenu.menu.items.push({
23221                     actiontype : this.cleanStyles[i],
23222                     html: 'Remove ' + this.cleanStyles[i],
23223                     handler: function(a,b) {
23224 //                        Roo.log(a);
23225 //                        Roo.log(b);
23226                         var c = Roo.get(editorcore.doc.body);
23227                         c.select('[style]').each(function(s) {
23228                             s.dom.style.removeProperty(a.actiontype);
23229                         });
23230                         editorcore.syncValue();
23231                     },
23232                     tabIndex:-1
23233                 });
23234             }
23235              cmenu.menu.items.push({
23236                 actiontype : 'tablewidths',
23237                 html: 'Remove Table Widths',
23238                 handler: function(a,b) {
23239                     editorcore.cleanTableWidths();
23240                     editorcore.syncValue();
23241                 },
23242                 tabIndex:-1
23243             });
23244             cmenu.menu.items.push({
23245                 actiontype : 'word',
23246                 html: 'Remove MS Word Formating',
23247                 handler: function(a,b) {
23248                     editorcore.cleanWord();
23249                     editorcore.syncValue();
23250                 },
23251                 tabIndex:-1
23252             });
23253             
23254             cmenu.menu.items.push({
23255                 actiontype : 'all',
23256                 html: 'Remove All Styles',
23257                 handler: function(a,b) {
23258                     
23259                     var c = Roo.get(editorcore.doc.body);
23260                     c.select('[style]').each(function(s) {
23261                         s.dom.removeAttribute('style');
23262                     });
23263                     editorcore.syncValue();
23264                 },
23265                 tabIndex:-1
23266             });
23267             
23268             cmenu.menu.items.push({
23269                 actiontype : 'all',
23270                 html: 'Remove All CSS Classes',
23271                 handler: function(a,b) {
23272                     
23273                     var c = Roo.get(editorcore.doc.body);
23274                     c.select('[class]').each(function(s) {
23275                         s.dom.removeAttribute('class');
23276                     });
23277                     editorcore.cleanWord();
23278                     editorcore.syncValue();
23279                 },
23280                 tabIndex:-1
23281             });
23282             
23283              cmenu.menu.items.push({
23284                 actiontype : 'tidy',
23285                 html: 'Tidy HTML Source',
23286                 handler: function(a,b) {
23287                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23288                     editorcore.syncValue();
23289                 },
23290                 tabIndex:-1
23291             });
23292             
23293             
23294             tb.add(cmenu);
23295         }
23296          
23297         if (!this.disable.specialElements) {
23298             var semenu = {
23299                 text: "Other;",
23300                 cls: 'x-edit-none',
23301                 menu : {
23302                     items : []
23303                 }
23304             };
23305             for (var i =0; i < this.specialElements.length; i++) {
23306                 semenu.menu.items.push(
23307                     Roo.apply({ 
23308                         handler: function(a,b) {
23309                             editor.insertAtCursor(this.ihtml);
23310                         }
23311                     }, this.specialElements[i])
23312                 );
23313                     
23314             }
23315             
23316             tb.add(semenu);
23317             
23318             
23319         }
23320          
23321         
23322         if (this.btns) {
23323             for(var i =0; i< this.btns.length;i++) {
23324                 var b = Roo.factory(this.btns[i],Roo.form);
23325                 b.cls =  'x-edit-none';
23326                 
23327                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23328                     b.cls += ' x-init-enable';
23329                 }
23330                 
23331                 b.scope = editorcore;
23332                 tb.add(b);
23333             }
23334         
23335         }
23336         
23337         
23338         
23339         // disable everything...
23340         
23341         this.tb.items.each(function(item){
23342             
23343            if(
23344                 item.id != editorcore.frameId+ '-sourceedit' && 
23345                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23346             ){
23347                 
23348                 item.disable();
23349             }
23350         });
23351         this.rendered = true;
23352         
23353         // the all the btns;
23354         editor.on('editorevent', this.updateToolbar, this);
23355         // other toolbars need to implement this..
23356         //editor.on('editmodechange', this.updateToolbar, this);
23357     },
23358     
23359     
23360     relayBtnCmd : function(btn) {
23361         this.editorcore.relayCmd(btn.cmd);
23362     },
23363     // private used internally
23364     createLink : function(){
23365         Roo.log("create link?");
23366         var url = prompt(this.createLinkText, this.defaultLinkValue);
23367         if(url && url != 'http:/'+'/'){
23368             this.editorcore.relayCmd('createlink', url);
23369         }
23370     },
23371
23372     
23373     /**
23374      * Protected method that will not generally be called directly. It triggers
23375      * a toolbar update by reading the markup state of the current selection in the editor.
23376      */
23377     updateToolbar: function(){
23378
23379         if(!this.editorcore.activated){
23380             this.editor.onFirstFocus();
23381             return;
23382         }
23383
23384         var btns = this.tb.items.map, 
23385             doc = this.editorcore.doc,
23386             frameId = this.editorcore.frameId;
23387
23388         if(!this.disable.font && !Roo.isSafari){
23389             /*
23390             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23391             if(name != this.fontSelect.dom.value){
23392                 this.fontSelect.dom.value = name;
23393             }
23394             */
23395         }
23396         if(!this.disable.format){
23397             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23398             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23399             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23400             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23401         }
23402         if(!this.disable.alignments){
23403             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23404             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23405             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23406         }
23407         if(!Roo.isSafari && !this.disable.lists){
23408             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23409             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23410         }
23411         
23412         var ans = this.editorcore.getAllAncestors();
23413         if (this.formatCombo) {
23414             
23415             
23416             var store = this.formatCombo.store;
23417             this.formatCombo.setValue("");
23418             for (var i =0; i < ans.length;i++) {
23419                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23420                     // select it..
23421                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23422                     break;
23423                 }
23424             }
23425         }
23426         
23427         
23428         
23429         // hides menus... - so this cant be on a menu...
23430         Roo.menu.MenuMgr.hideAll();
23431
23432         //this.editorsyncValue();
23433     },
23434    
23435     
23436     createFontOptions : function(){
23437         var buf = [], fs = this.fontFamilies, ff, lc;
23438         
23439         
23440         
23441         for(var i = 0, len = fs.length; i< len; i++){
23442             ff = fs[i];
23443             lc = ff.toLowerCase();
23444             buf.push(
23445                 '<option value="',lc,'" style="font-family:',ff,';"',
23446                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23447                     ff,
23448                 '</option>'
23449             );
23450         }
23451         return buf.join('');
23452     },
23453     
23454     toggleSourceEdit : function(sourceEditMode){
23455         
23456         Roo.log("toolbar toogle");
23457         if(sourceEditMode === undefined){
23458             sourceEditMode = !this.sourceEditMode;
23459         }
23460         this.sourceEditMode = sourceEditMode === true;
23461         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23462         // just toggle the button?
23463         if(btn.pressed !== this.sourceEditMode){
23464             btn.toggle(this.sourceEditMode);
23465             return;
23466         }
23467         
23468         if(sourceEditMode){
23469             Roo.log("disabling buttons");
23470             this.tb.items.each(function(item){
23471                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23472                     item.disable();
23473                 }
23474             });
23475           
23476         }else{
23477             Roo.log("enabling buttons");
23478             if(this.editorcore.initialized){
23479                 this.tb.items.each(function(item){
23480                     item.enable();
23481                 });
23482             }
23483             
23484         }
23485         Roo.log("calling toggole on editor");
23486         // tell the editor that it's been pressed..
23487         this.editor.toggleSourceEdit(sourceEditMode);
23488        
23489     },
23490      /**
23491      * Object collection of toolbar tooltips for the buttons in the editor. The key
23492      * is the command id associated with that button and the value is a valid QuickTips object.
23493      * For example:
23494 <pre><code>
23495 {
23496     bold : {
23497         title: 'Bold (Ctrl+B)',
23498         text: 'Make the selected text bold.',
23499         cls: 'x-html-editor-tip'
23500     },
23501     italic : {
23502         title: 'Italic (Ctrl+I)',
23503         text: 'Make the selected text italic.',
23504         cls: 'x-html-editor-tip'
23505     },
23506     ...
23507 </code></pre>
23508     * @type Object
23509      */
23510     buttonTips : {
23511         bold : {
23512             title: 'Bold (Ctrl+B)',
23513             text: 'Make the selected text bold.',
23514             cls: 'x-html-editor-tip'
23515         },
23516         italic : {
23517             title: 'Italic (Ctrl+I)',
23518             text: 'Make the selected text italic.',
23519             cls: 'x-html-editor-tip'
23520         },
23521         underline : {
23522             title: 'Underline (Ctrl+U)',
23523             text: 'Underline the selected text.',
23524             cls: 'x-html-editor-tip'
23525         },
23526         strikethrough : {
23527             title: 'Strikethrough',
23528             text: 'Strikethrough the selected text.',
23529             cls: 'x-html-editor-tip'
23530         },
23531         increasefontsize : {
23532             title: 'Grow Text',
23533             text: 'Increase the font size.',
23534             cls: 'x-html-editor-tip'
23535         },
23536         decreasefontsize : {
23537             title: 'Shrink Text',
23538             text: 'Decrease the font size.',
23539             cls: 'x-html-editor-tip'
23540         },
23541         backcolor : {
23542             title: 'Text Highlight Color',
23543             text: 'Change the background color of the selected text.',
23544             cls: 'x-html-editor-tip'
23545         },
23546         forecolor : {
23547             title: 'Font Color',
23548             text: 'Change the color of the selected text.',
23549             cls: 'x-html-editor-tip'
23550         },
23551         justifyleft : {
23552             title: 'Align Text Left',
23553             text: 'Align text to the left.',
23554             cls: 'x-html-editor-tip'
23555         },
23556         justifycenter : {
23557             title: 'Center Text',
23558             text: 'Center text in the editor.',
23559             cls: 'x-html-editor-tip'
23560         },
23561         justifyright : {
23562             title: 'Align Text Right',
23563             text: 'Align text to the right.',
23564             cls: 'x-html-editor-tip'
23565         },
23566         insertunorderedlist : {
23567             title: 'Bullet List',
23568             text: 'Start a bulleted list.',
23569             cls: 'x-html-editor-tip'
23570         },
23571         insertorderedlist : {
23572             title: 'Numbered List',
23573             text: 'Start a numbered list.',
23574             cls: 'x-html-editor-tip'
23575         },
23576         createlink : {
23577             title: 'Hyperlink',
23578             text: 'Make the selected text a hyperlink.',
23579             cls: 'x-html-editor-tip'
23580         },
23581         sourceedit : {
23582             title: 'Source Edit',
23583             text: 'Switch to source editing mode.',
23584             cls: 'x-html-editor-tip'
23585         }
23586     },
23587     // private
23588     onDestroy : function(){
23589         if(this.rendered){
23590             
23591             this.tb.items.each(function(item){
23592                 if(item.menu){
23593                     item.menu.removeAll();
23594                     if(item.menu.el){
23595                         item.menu.el.destroy();
23596                     }
23597                 }
23598                 item.destroy();
23599             });
23600              
23601         }
23602     },
23603     onFirstFocus: function() {
23604         this.tb.items.each(function(item){
23605            item.enable();
23606         });
23607     }
23608 });
23609
23610
23611
23612
23613 // <script type="text/javascript">
23614 /*
23615  * Based on
23616  * Ext JS Library 1.1.1
23617  * Copyright(c) 2006-2007, Ext JS, LLC.
23618  *  
23619  
23620  */
23621
23622  
23623 /**
23624  * @class Roo.form.HtmlEditor.ToolbarContext
23625  * Context Toolbar
23626  * 
23627  * Usage:
23628  *
23629  new Roo.form.HtmlEditor({
23630     ....
23631     toolbars : [
23632         { xtype: 'ToolbarStandard', styles : {} }
23633         { xtype: 'ToolbarContext', disable : {} }
23634     ]
23635 })
23636
23637      
23638  * 
23639  * @config : {Object} disable List of elements to disable.. (not done yet.)
23640  * @config : {Object} styles  Map of styles available.
23641  * 
23642  */
23643
23644 Roo.form.HtmlEditor.ToolbarContext = function(config)
23645 {
23646     
23647     Roo.apply(this, config);
23648     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23649     // dont call parent... till later.
23650     this.styles = this.styles || {};
23651 }
23652
23653  
23654
23655 Roo.form.HtmlEditor.ToolbarContext.types = {
23656     'IMG' : {
23657         width : {
23658             title: "Width",
23659             width: 40
23660         },
23661         height:  {
23662             title: "Height",
23663             width: 40
23664         },
23665         align: {
23666             title: "Align",
23667             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23668             width : 80
23669             
23670         },
23671         border: {
23672             title: "Border",
23673             width: 40
23674         },
23675         alt: {
23676             title: "Alt",
23677             width: 120
23678         },
23679         src : {
23680             title: "Src",
23681             width: 220
23682         }
23683         
23684     },
23685     'A' : {
23686         name : {
23687             title: "Name",
23688             width: 50
23689         },
23690         target:  {
23691             title: "Target",
23692             width: 120
23693         },
23694         href:  {
23695             title: "Href",
23696             width: 220
23697         } // border?
23698         
23699     },
23700     'TABLE' : {
23701         rows : {
23702             title: "Rows",
23703             width: 20
23704         },
23705         cols : {
23706             title: "Cols",
23707             width: 20
23708         },
23709         width : {
23710             title: "Width",
23711             width: 40
23712         },
23713         height : {
23714             title: "Height",
23715             width: 40
23716         },
23717         border : {
23718             title: "Border",
23719             width: 20
23720         }
23721     },
23722     'TD' : {
23723         width : {
23724             title: "Width",
23725             width: 40
23726         },
23727         height : {
23728             title: "Height",
23729             width: 40
23730         },   
23731         align: {
23732             title: "Align",
23733             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23734             width: 80
23735         },
23736         valign: {
23737             title: "Valign",
23738             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23739             width: 80
23740         },
23741         colspan: {
23742             title: "Colspan",
23743             width: 20
23744             
23745         },
23746          'font-family'  : {
23747             title : "Font",
23748             style : 'fontFamily',
23749             displayField: 'display',
23750             optname : 'font-family',
23751             width: 140
23752         }
23753     },
23754     'INPUT' : {
23755         name : {
23756             title: "name",
23757             width: 120
23758         },
23759         value : {
23760             title: "Value",
23761             width: 120
23762         },
23763         width : {
23764             title: "Width",
23765             width: 40
23766         }
23767     },
23768     'LABEL' : {
23769         'for' : {
23770             title: "For",
23771             width: 120
23772         }
23773     },
23774     'TEXTAREA' : {
23775           name : {
23776             title: "name",
23777             width: 120
23778         },
23779         rows : {
23780             title: "Rows",
23781             width: 20
23782         },
23783         cols : {
23784             title: "Cols",
23785             width: 20
23786         }
23787     },
23788     'SELECT' : {
23789         name : {
23790             title: "name",
23791             width: 120
23792         },
23793         selectoptions : {
23794             title: "Options",
23795             width: 200
23796         }
23797     },
23798     
23799     // should we really allow this??
23800     // should this just be 
23801     'BODY' : {
23802         title : {
23803             title: "Title",
23804             width: 200,
23805             disabled : true
23806         }
23807     },
23808     'SPAN' : {
23809         'font-family'  : {
23810             title : "Font",
23811             style : 'fontFamily',
23812             displayField: 'display',
23813             optname : 'font-family',
23814             width: 140
23815         }
23816     },
23817     'DIV' : {
23818         'font-family'  : {
23819             title : "Font",
23820             style : 'fontFamily',
23821             displayField: 'display',
23822             optname : 'font-family',
23823             width: 140
23824         }
23825     },
23826      'P' : {
23827         'font-family'  : {
23828             title : "Font",
23829             style : 'fontFamily',
23830             displayField: 'display',
23831             optname : 'font-family',
23832             width: 140
23833         }
23834     },
23835     
23836     '*' : {
23837         // empty..
23838     }
23839
23840 };
23841
23842 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23843 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23844
23845 Roo.form.HtmlEditor.ToolbarContext.options = {
23846         'font-family'  : [ 
23847                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23848                 [ 'Courier New', 'Courier New'],
23849                 [ 'Tahoma', 'Tahoma'],
23850                 [ 'Times New Roman,serif', 'Times'],
23851                 [ 'Verdana','Verdana' ]
23852         ]
23853 };
23854
23855 // fixme - these need to be configurable..
23856  
23857
23858 //Roo.form.HtmlEditor.ToolbarContext.types
23859
23860
23861 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23862     
23863     tb: false,
23864     
23865     rendered: false,
23866     
23867     editor : false,
23868     editorcore : false,
23869     /**
23870      * @cfg {Object} disable  List of toolbar elements to disable
23871          
23872      */
23873     disable : false,
23874     /**
23875      * @cfg {Object} styles List of styles 
23876      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23877      *
23878      * These must be defined in the page, so they get rendered correctly..
23879      * .headline { }
23880      * TD.underline { }
23881      * 
23882      */
23883     styles : false,
23884     
23885     options: false,
23886     
23887     toolbars : false,
23888     
23889     init : function(editor)
23890     {
23891         this.editor = editor;
23892         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23893         var editorcore = this.editorcore;
23894         
23895         var fid = editorcore.frameId;
23896         var etb = this;
23897         function btn(id, toggle, handler){
23898             var xid = fid + '-'+ id ;
23899             return {
23900                 id : xid,
23901                 cmd : id,
23902                 cls : 'x-btn-icon x-edit-'+id,
23903                 enableToggle:toggle !== false,
23904                 scope: editorcore, // was editor...
23905                 handler:handler||editorcore.relayBtnCmd,
23906                 clickEvent:'mousedown',
23907                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23908                 tabIndex:-1
23909             };
23910         }
23911         // create a new element.
23912         var wdiv = editor.wrap.createChild({
23913                 tag: 'div'
23914             }, editor.wrap.dom.firstChild.nextSibling, true);
23915         
23916         // can we do this more than once??
23917         
23918          // stop form submits
23919       
23920  
23921         // disable everything...
23922         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23923         this.toolbars = {};
23924            
23925         for (var i in  ty) {
23926           
23927             this.toolbars[i] = this.buildToolbar(ty[i],i);
23928         }
23929         this.tb = this.toolbars.BODY;
23930         this.tb.el.show();
23931         this.buildFooter();
23932         this.footer.show();
23933         editor.on('hide', function( ) { this.footer.hide() }, this);
23934         editor.on('show', function( ) { this.footer.show() }, this);
23935         
23936          
23937         this.rendered = true;
23938         
23939         // the all the btns;
23940         editor.on('editorevent', this.updateToolbar, this);
23941         // other toolbars need to implement this..
23942         //editor.on('editmodechange', this.updateToolbar, this);
23943     },
23944     
23945     
23946     
23947     /**
23948      * Protected method that will not generally be called directly. It triggers
23949      * a toolbar update by reading the markup state of the current selection in the editor.
23950      *
23951      * Note you can force an update by calling on('editorevent', scope, false)
23952      */
23953     updateToolbar: function(editor,ev,sel){
23954
23955         //Roo.log(ev);
23956         // capture mouse up - this is handy for selecting images..
23957         // perhaps should go somewhere else...
23958         if(!this.editorcore.activated){
23959              this.editor.onFirstFocus();
23960             return;
23961         }
23962         
23963         
23964         
23965         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23966         // selectNode - might want to handle IE?
23967         if (ev &&
23968             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23969             ev.target && ev.target.tagName == 'IMG') {
23970             // they have click on an image...
23971             // let's see if we can change the selection...
23972             sel = ev.target;
23973          
23974               var nodeRange = sel.ownerDocument.createRange();
23975             try {
23976                 nodeRange.selectNode(sel);
23977             } catch (e) {
23978                 nodeRange.selectNodeContents(sel);
23979             }
23980             //nodeRange.collapse(true);
23981             var s = this.editorcore.win.getSelection();
23982             s.removeAllRanges();
23983             s.addRange(nodeRange);
23984         }  
23985         
23986       
23987         var updateFooter = sel ? false : true;
23988         
23989         
23990         var ans = this.editorcore.getAllAncestors();
23991         
23992         // pick
23993         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23994         
23995         if (!sel) { 
23996             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
23997             sel = sel ? sel : this.editorcore.doc.body;
23998             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
23999             
24000         }
24001         // pick a menu that exists..
24002         var tn = sel.tagName.toUpperCase();
24003         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24004         
24005         tn = sel.tagName.toUpperCase();
24006         
24007         var lastSel = this.tb.selectedNode;
24008         
24009         this.tb.selectedNode = sel;
24010         
24011         // if current menu does not match..
24012         
24013         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24014                 
24015             this.tb.el.hide();
24016             ///console.log("show: " + tn);
24017             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24018             this.tb.el.show();
24019             // update name
24020             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24021             
24022             
24023             // update attributes
24024             if (this.tb.fields) {
24025                 this.tb.fields.each(function(e) {
24026                     if (e.stylename) {
24027                         e.setValue(sel.style[e.stylename]);
24028                         return;
24029                     } 
24030                    e.setValue(sel.getAttribute(e.attrname));
24031                 });
24032             }
24033             
24034             var hasStyles = false;
24035             for(var i in this.styles) {
24036                 hasStyles = true;
24037                 break;
24038             }
24039             
24040             // update styles
24041             if (hasStyles) { 
24042                 var st = this.tb.fields.item(0);
24043                 
24044                 st.store.removeAll();
24045                
24046                 
24047                 var cn = sel.className.split(/\s+/);
24048                 
24049                 var avs = [];
24050                 if (this.styles['*']) {
24051                     
24052                     Roo.each(this.styles['*'], function(v) {
24053                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24054                     });
24055                 }
24056                 if (this.styles[tn]) { 
24057                     Roo.each(this.styles[tn], function(v) {
24058                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24059                     });
24060                 }
24061                 
24062                 st.store.loadData(avs);
24063                 st.collapse();
24064                 st.setValue(cn);
24065             }
24066             // flag our selected Node.
24067             this.tb.selectedNode = sel;
24068            
24069            
24070             Roo.menu.MenuMgr.hideAll();
24071
24072         }
24073         
24074         if (!updateFooter) {
24075             //this.footDisp.dom.innerHTML = ''; 
24076             return;
24077         }
24078         // update the footer
24079         //
24080         var html = '';
24081         
24082         this.footerEls = ans.reverse();
24083         Roo.each(this.footerEls, function(a,i) {
24084             if (!a) { return; }
24085             html += html.length ? ' &gt; '  :  '';
24086             
24087             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24088             
24089         });
24090        
24091         // 
24092         var sz = this.footDisp.up('td').getSize();
24093         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24094         this.footDisp.dom.style.marginLeft = '5px';
24095         
24096         this.footDisp.dom.style.overflow = 'hidden';
24097         
24098         this.footDisp.dom.innerHTML = html;
24099             
24100         //this.editorsyncValue();
24101     },
24102      
24103     
24104    
24105        
24106     // private
24107     onDestroy : function(){
24108         if(this.rendered){
24109             
24110             this.tb.items.each(function(item){
24111                 if(item.menu){
24112                     item.menu.removeAll();
24113                     if(item.menu.el){
24114                         item.menu.el.destroy();
24115                     }
24116                 }
24117                 item.destroy();
24118             });
24119              
24120         }
24121     },
24122     onFirstFocus: function() {
24123         // need to do this for all the toolbars..
24124         this.tb.items.each(function(item){
24125            item.enable();
24126         });
24127     },
24128     buildToolbar: function(tlist, nm)
24129     {
24130         var editor = this.editor;
24131         var editorcore = this.editorcore;
24132          // create a new element.
24133         var wdiv = editor.wrap.createChild({
24134                 tag: 'div'
24135             }, editor.wrap.dom.firstChild.nextSibling, true);
24136         
24137        
24138         var tb = new Roo.Toolbar(wdiv);
24139         // add the name..
24140         
24141         tb.add(nm+ ":&nbsp;");
24142         
24143         var styles = [];
24144         for(var i in this.styles) {
24145             styles.push(i);
24146         }
24147         
24148         // styles...
24149         if (styles && styles.length) {
24150             
24151             // this needs a multi-select checkbox...
24152             tb.addField( new Roo.form.ComboBox({
24153                 store: new Roo.data.SimpleStore({
24154                     id : 'val',
24155                     fields: ['val', 'selected'],
24156                     data : [] 
24157                 }),
24158                 name : '-roo-edit-className',
24159                 attrname : 'className',
24160                 displayField: 'val',
24161                 typeAhead: false,
24162                 mode: 'local',
24163                 editable : false,
24164                 triggerAction: 'all',
24165                 emptyText:'Select Style',
24166                 selectOnFocus:true,
24167                 width: 130,
24168                 listeners : {
24169                     'select': function(c, r, i) {
24170                         // initial support only for on class per el..
24171                         tb.selectedNode.className =  r ? r.get('val') : '';
24172                         editorcore.syncValue();
24173                     }
24174                 }
24175     
24176             }));
24177         }
24178         
24179         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24180         var tbops = tbc.options;
24181         
24182         for (var i in tlist) {
24183             
24184             var item = tlist[i];
24185             tb.add(item.title + ":&nbsp;");
24186             
24187             
24188             //optname == used so you can configure the options available..
24189             var opts = item.opts ? item.opts : false;
24190             if (item.optname) {
24191                 opts = tbops[item.optname];
24192            
24193             }
24194             
24195             if (opts) {
24196                 // opts == pulldown..
24197                 tb.addField( new Roo.form.ComboBox({
24198                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24199                         id : 'val',
24200                         fields: ['val', 'display'],
24201                         data : opts  
24202                     }),
24203                     name : '-roo-edit-' + i,
24204                     attrname : i,
24205                     stylename : item.style ? item.style : false,
24206                     displayField: item.displayField ? item.displayField : 'val',
24207                     valueField :  'val',
24208                     typeAhead: false,
24209                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24210                     editable : false,
24211                     triggerAction: 'all',
24212                     emptyText:'Select',
24213                     selectOnFocus:true,
24214                     width: item.width ? item.width  : 130,
24215                     listeners : {
24216                         'select': function(c, r, i) {
24217                             if (c.stylename) {
24218                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24219                                 return;
24220                             }
24221                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24222                         }
24223                     }
24224
24225                 }));
24226                 continue;
24227                     
24228                  
24229                 
24230                 tb.addField( new Roo.form.TextField({
24231                     name: i,
24232                     width: 100,
24233                     //allowBlank:false,
24234                     value: ''
24235                 }));
24236                 continue;
24237             }
24238             tb.addField( new Roo.form.TextField({
24239                 name: '-roo-edit-' + i,
24240                 attrname : i,
24241                 
24242                 width: item.width,
24243                 //allowBlank:true,
24244                 value: '',
24245                 listeners: {
24246                     'change' : function(f, nv, ov) {
24247                         tb.selectedNode.setAttribute(f.attrname, nv);
24248                         editorcore.syncValue();
24249                     }
24250                 }
24251             }));
24252              
24253         }
24254         
24255         var _this = this;
24256         
24257         if(nm == 'BODY'){
24258             tb.addSeparator();
24259         
24260             tb.addButton( {
24261                 text: 'Stylesheets',
24262
24263                 listeners : {
24264                     click : function ()
24265                     {
24266                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24267                     }
24268                 }
24269             });
24270         }
24271         
24272         tb.addFill();
24273         tb.addButton( {
24274             text: 'Remove Tag',
24275     
24276             listeners : {
24277                 click : function ()
24278                 {
24279                     // remove
24280                     // undo does not work.
24281                      
24282                     var sn = tb.selectedNode;
24283                     
24284                     var pn = sn.parentNode;
24285                     
24286                     var stn =  sn.childNodes[0];
24287                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24288                     while (sn.childNodes.length) {
24289                         var node = sn.childNodes[0];
24290                         sn.removeChild(node);
24291                         //Roo.log(node);
24292                         pn.insertBefore(node, sn);
24293                         
24294                     }
24295                     pn.removeChild(sn);
24296                     var range = editorcore.createRange();
24297         
24298                     range.setStart(stn,0);
24299                     range.setEnd(en,0); //????
24300                     //range.selectNode(sel);
24301                     
24302                     
24303                     var selection = editorcore.getSelection();
24304                     selection.removeAllRanges();
24305                     selection.addRange(range);
24306                     
24307                     
24308                     
24309                     //_this.updateToolbar(null, null, pn);
24310                     _this.updateToolbar(null, null, null);
24311                     _this.footDisp.dom.innerHTML = ''; 
24312                 }
24313             }
24314             
24315                     
24316                 
24317             
24318         });
24319         
24320         
24321         tb.el.on('click', function(e){
24322             e.preventDefault(); // what does this do?
24323         });
24324         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24325         tb.el.hide();
24326         tb.name = nm;
24327         // dont need to disable them... as they will get hidden
24328         return tb;
24329          
24330         
24331     },
24332     buildFooter : function()
24333     {
24334         
24335         var fel = this.editor.wrap.createChild();
24336         this.footer = new Roo.Toolbar(fel);
24337         // toolbar has scrolly on left / right?
24338         var footDisp= new Roo.Toolbar.Fill();
24339         var _t = this;
24340         this.footer.add(
24341             {
24342                 text : '&lt;',
24343                 xtype: 'Button',
24344                 handler : function() {
24345                     _t.footDisp.scrollTo('left',0,true)
24346                 }
24347             }
24348         );
24349         this.footer.add( footDisp );
24350         this.footer.add( 
24351             {
24352                 text : '&gt;',
24353                 xtype: 'Button',
24354                 handler : function() {
24355                     // no animation..
24356                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24357                 }
24358             }
24359         );
24360         var fel = Roo.get(footDisp.el);
24361         fel.addClass('x-editor-context');
24362         this.footDispWrap = fel; 
24363         this.footDispWrap.overflow  = 'hidden';
24364         
24365         this.footDisp = fel.createChild();
24366         this.footDispWrap.on('click', this.onContextClick, this)
24367         
24368         
24369     },
24370     onContextClick : function (ev,dom)
24371     {
24372         ev.preventDefault();
24373         var  cn = dom.className;
24374         //Roo.log(cn);
24375         if (!cn.match(/x-ed-loc-/)) {
24376             return;
24377         }
24378         var n = cn.split('-').pop();
24379         var ans = this.footerEls;
24380         var sel = ans[n];
24381         
24382          // pick
24383         var range = this.editorcore.createRange();
24384         
24385         range.selectNodeContents(sel);
24386         //range.selectNode(sel);
24387         
24388         
24389         var selection = this.editorcore.getSelection();
24390         selection.removeAllRanges();
24391         selection.addRange(range);
24392         
24393         
24394         
24395         this.updateToolbar(null, null, sel);
24396         
24397         
24398     }
24399     
24400     
24401     
24402     
24403     
24404 });
24405
24406
24407
24408
24409
24410 /*
24411  * Based on:
24412  * Ext JS Library 1.1.1
24413  * Copyright(c) 2006-2007, Ext JS, LLC.
24414  *
24415  * Originally Released Under LGPL - original licence link has changed is not relivant.
24416  *
24417  * Fork - LGPL
24418  * <script type="text/javascript">
24419  */
24420  
24421 /**
24422  * @class Roo.form.BasicForm
24423  * @extends Roo.util.Observable
24424  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24425  * @constructor
24426  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24427  * @param {Object} config Configuration options
24428  */
24429 Roo.form.BasicForm = function(el, config){
24430     this.allItems = [];
24431     this.childForms = [];
24432     Roo.apply(this, config);
24433     /*
24434      * The Roo.form.Field items in this form.
24435      * @type MixedCollection
24436      */
24437      
24438      
24439     this.items = new Roo.util.MixedCollection(false, function(o){
24440         return o.id || (o.id = Roo.id());
24441     });
24442     this.addEvents({
24443         /**
24444          * @event beforeaction
24445          * Fires before any action is performed. Return false to cancel the action.
24446          * @param {Form} this
24447          * @param {Action} action The action to be performed
24448          */
24449         beforeaction: true,
24450         /**
24451          * @event actionfailed
24452          * Fires when an action fails.
24453          * @param {Form} this
24454          * @param {Action} action The action that failed
24455          */
24456         actionfailed : true,
24457         /**
24458          * @event actioncomplete
24459          * Fires when an action is completed.
24460          * @param {Form} this
24461          * @param {Action} action The action that completed
24462          */
24463         actioncomplete : true
24464     });
24465     if(el){
24466         this.initEl(el);
24467     }
24468     Roo.form.BasicForm.superclass.constructor.call(this);
24469     
24470     Roo.form.BasicForm.popover.apply();
24471 };
24472
24473 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24474     /**
24475      * @cfg {String} method
24476      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24477      */
24478     /**
24479      * @cfg {DataReader} reader
24480      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24481      * This is optional as there is built-in support for processing JSON.
24482      */
24483     /**
24484      * @cfg {DataReader} errorReader
24485      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24486      * This is completely optional as there is built-in support for processing JSON.
24487      */
24488     /**
24489      * @cfg {String} url
24490      * The URL to use for form actions if one isn't supplied in the action options.
24491      */
24492     /**
24493      * @cfg {Boolean} fileUpload
24494      * Set to true if this form is a file upload.
24495      */
24496      
24497     /**
24498      * @cfg {Object} baseParams
24499      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24500      */
24501      /**
24502      
24503     /**
24504      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24505      */
24506     timeout: 30,
24507
24508     // private
24509     activeAction : null,
24510
24511     /**
24512      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24513      * or setValues() data instead of when the form was first created.
24514      */
24515     trackResetOnLoad : false,
24516     
24517     
24518     /**
24519      * childForms - used for multi-tab forms
24520      * @type {Array}
24521      */
24522     childForms : false,
24523     
24524     /**
24525      * allItems - full list of fields.
24526      * @type {Array}
24527      */
24528     allItems : false,
24529     
24530     /**
24531      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24532      * element by passing it or its id or mask the form itself by passing in true.
24533      * @type Mixed
24534      */
24535     waitMsgTarget : false,
24536     
24537     /**
24538      * @type Boolean
24539      */
24540     disableMask : false,
24541     
24542     /**
24543      * @cfg {Boolean} errorMask (true|false) default false
24544      */
24545     errorMask : false,
24546     
24547     /**
24548      * @cfg {Number} maskOffset Default 100
24549      */
24550     maskOffset : 100,
24551
24552     // private
24553     initEl : function(el){
24554         this.el = Roo.get(el);
24555         this.id = this.el.id || Roo.id();
24556         this.el.on('submit', this.onSubmit, this);
24557         this.el.addClass('x-form');
24558     },
24559
24560     // private
24561     onSubmit : function(e){
24562         e.stopEvent();
24563     },
24564
24565     /**
24566      * Returns true if client-side validation on the form is successful.
24567      * @return Boolean
24568      */
24569     isValid : function(){
24570         var valid = true;
24571         var target = false;
24572         this.items.each(function(f){
24573             if(f.validate()){
24574                 return;
24575             }
24576             
24577             valid = false;
24578                 
24579             if(!target && f.el.isVisible(true)){
24580                 target = f;
24581             }
24582         });
24583         
24584         if(this.errorMask && !valid){
24585             Roo.form.BasicForm.popover.mask(this, target);
24586         }
24587         
24588         return valid;
24589     },
24590
24591     /**
24592      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24593      * @return Boolean
24594      */
24595     isDirty : function(){
24596         var dirty = false;
24597         this.items.each(function(f){
24598            if(f.isDirty()){
24599                dirty = true;
24600                return false;
24601            }
24602         });
24603         return dirty;
24604     },
24605     
24606     /**
24607      * Returns true if any fields in this form have changed since their original load. (New version)
24608      * @return Boolean
24609      */
24610     
24611     hasChanged : function()
24612     {
24613         var dirty = false;
24614         this.items.each(function(f){
24615            if(f.hasChanged()){
24616                dirty = true;
24617                return false;
24618            }
24619         });
24620         return dirty;
24621         
24622     },
24623     /**
24624      * Resets all hasChanged to 'false' -
24625      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24626      * So hasChanged storage is only to be used for this purpose
24627      * @return Boolean
24628      */
24629     resetHasChanged : function()
24630     {
24631         this.items.each(function(f){
24632            f.resetHasChanged();
24633         });
24634         
24635     },
24636     
24637     
24638     /**
24639      * Performs a predefined action (submit or load) or custom actions you define on this form.
24640      * @param {String} actionName The name of the action type
24641      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24642      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24643      * accept other config options):
24644      * <pre>
24645 Property          Type             Description
24646 ----------------  ---------------  ----------------------------------------------------------------------------------
24647 url               String           The url for the action (defaults to the form's url)
24648 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24649 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24650 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24651                                    validate the form on the client (defaults to false)
24652      * </pre>
24653      * @return {BasicForm} this
24654      */
24655     doAction : function(action, options){
24656         if(typeof action == 'string'){
24657             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24658         }
24659         if(this.fireEvent('beforeaction', this, action) !== false){
24660             this.beforeAction(action);
24661             action.run.defer(100, action);
24662         }
24663         return this;
24664     },
24665
24666     /**
24667      * Shortcut to do a submit action.
24668      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24669      * @return {BasicForm} this
24670      */
24671     submit : function(options){
24672         this.doAction('submit', options);
24673         return this;
24674     },
24675
24676     /**
24677      * Shortcut to do a load action.
24678      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24679      * @return {BasicForm} this
24680      */
24681     load : function(options){
24682         this.doAction('load', options);
24683         return this;
24684     },
24685
24686     /**
24687      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24688      * @param {Record} record The record to edit
24689      * @return {BasicForm} this
24690      */
24691     updateRecord : function(record){
24692         record.beginEdit();
24693         var fs = record.fields;
24694         fs.each(function(f){
24695             var field = this.findField(f.name);
24696             if(field){
24697                 record.set(f.name, field.getValue());
24698             }
24699         }, this);
24700         record.endEdit();
24701         return this;
24702     },
24703
24704     /**
24705      * Loads an Roo.data.Record into this form.
24706      * @param {Record} record The record to load
24707      * @return {BasicForm} this
24708      */
24709     loadRecord : function(record){
24710         this.setValues(record.data);
24711         return this;
24712     },
24713
24714     // private
24715     beforeAction : function(action){
24716         var o = action.options;
24717         
24718         if(!this.disableMask) {
24719             if(this.waitMsgTarget === true){
24720                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24721             }else if(this.waitMsgTarget){
24722                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24723                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24724             }else {
24725                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24726             }
24727         }
24728         
24729          
24730     },
24731
24732     // private
24733     afterAction : function(action, success){
24734         this.activeAction = null;
24735         var o = action.options;
24736         
24737         if(!this.disableMask) {
24738             if(this.waitMsgTarget === true){
24739                 this.el.unmask();
24740             }else if(this.waitMsgTarget){
24741                 this.waitMsgTarget.unmask();
24742             }else{
24743                 Roo.MessageBox.updateProgress(1);
24744                 Roo.MessageBox.hide();
24745             }
24746         }
24747         
24748         if(success){
24749             if(o.reset){
24750                 this.reset();
24751             }
24752             Roo.callback(o.success, o.scope, [this, action]);
24753             this.fireEvent('actioncomplete', this, action);
24754             
24755         }else{
24756             
24757             // failure condition..
24758             // we have a scenario where updates need confirming.
24759             // eg. if a locking scenario exists..
24760             // we look for { errors : { needs_confirm : true }} in the response.
24761             if (
24762                 (typeof(action.result) != 'undefined')  &&
24763                 (typeof(action.result.errors) != 'undefined')  &&
24764                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24765            ){
24766                 var _t = this;
24767                 Roo.MessageBox.confirm(
24768                     "Change requires confirmation",
24769                     action.result.errorMsg,
24770                     function(r) {
24771                         if (r != 'yes') {
24772                             return;
24773                         }
24774                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24775                     }
24776                     
24777                 );
24778                 
24779                 
24780                 
24781                 return;
24782             }
24783             
24784             Roo.callback(o.failure, o.scope, [this, action]);
24785             // show an error message if no failed handler is set..
24786             if (!this.hasListener('actionfailed')) {
24787                 Roo.MessageBox.alert("Error",
24788                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24789                         action.result.errorMsg :
24790                         "Saving Failed, please check your entries or try again"
24791                 );
24792             }
24793             
24794             this.fireEvent('actionfailed', this, action);
24795         }
24796         
24797     },
24798
24799     /**
24800      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24801      * @param {String} id The value to search for
24802      * @return Field
24803      */
24804     findField : function(id){
24805         var field = this.items.get(id);
24806         if(!field){
24807             this.items.each(function(f){
24808                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24809                     field = f;
24810                     return false;
24811                 }
24812             });
24813         }
24814         return field || null;
24815     },
24816
24817     /**
24818      * Add a secondary form to this one, 
24819      * Used to provide tabbed forms. One form is primary, with hidden values 
24820      * which mirror the elements from the other forms.
24821      * 
24822      * @param {Roo.form.Form} form to add.
24823      * 
24824      */
24825     addForm : function(form)
24826     {
24827        
24828         if (this.childForms.indexOf(form) > -1) {
24829             // already added..
24830             return;
24831         }
24832         this.childForms.push(form);
24833         var n = '';
24834         Roo.each(form.allItems, function (fe) {
24835             
24836             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24837             if (this.findField(n)) { // already added..
24838                 return;
24839             }
24840             var add = new Roo.form.Hidden({
24841                 name : n
24842             });
24843             add.render(this.el);
24844             
24845             this.add( add );
24846         }, this);
24847         
24848     },
24849     /**
24850      * Mark fields in this form invalid in bulk.
24851      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24852      * @return {BasicForm} this
24853      */
24854     markInvalid : function(errors){
24855         if(errors instanceof Array){
24856             for(var i = 0, len = errors.length; i < len; i++){
24857                 var fieldError = errors[i];
24858                 var f = this.findField(fieldError.id);
24859                 if(f){
24860                     f.markInvalid(fieldError.msg);
24861                 }
24862             }
24863         }else{
24864             var field, id;
24865             for(id in errors){
24866                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24867                     field.markInvalid(errors[id]);
24868                 }
24869             }
24870         }
24871         Roo.each(this.childForms || [], function (f) {
24872             f.markInvalid(errors);
24873         });
24874         
24875         return this;
24876     },
24877
24878     /**
24879      * Set values for fields in this form in bulk.
24880      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24881      * @return {BasicForm} this
24882      */
24883     setValues : function(values){
24884         if(values instanceof Array){ // array of objects
24885             for(var i = 0, len = values.length; i < len; i++){
24886                 var v = values[i];
24887                 var f = this.findField(v.id);
24888                 if(f){
24889                     f.setValue(v.value);
24890                     if(this.trackResetOnLoad){
24891                         f.originalValue = f.getValue();
24892                     }
24893                 }
24894             }
24895         }else{ // object hash
24896             var field, id;
24897             for(id in values){
24898                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24899                     
24900                     if (field.setFromData && 
24901                         field.valueField && 
24902                         field.displayField &&
24903                         // combos' with local stores can 
24904                         // be queried via setValue()
24905                         // to set their value..
24906                         (field.store && !field.store.isLocal)
24907                         ) {
24908                         // it's a combo
24909                         var sd = { };
24910                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24911                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24912                         field.setFromData(sd);
24913                         
24914                     } else {
24915                         field.setValue(values[id]);
24916                     }
24917                     
24918                     
24919                     if(this.trackResetOnLoad){
24920                         field.originalValue = field.getValue();
24921                     }
24922                 }
24923             }
24924         }
24925         this.resetHasChanged();
24926         
24927         
24928         Roo.each(this.childForms || [], function (f) {
24929             f.setValues(values);
24930             f.resetHasChanged();
24931         });
24932                 
24933         return this;
24934     },
24935  
24936     /**
24937      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24938      * they are returned as an array.
24939      * @param {Boolean} asString
24940      * @return {Object}
24941      */
24942     getValues : function(asString){
24943         if (this.childForms) {
24944             // copy values from the child forms
24945             Roo.each(this.childForms, function (f) {
24946                 this.setValues(f.getValues());
24947             }, this);
24948         }
24949         
24950         // use formdata
24951         if (typeof(FormData) != 'undefined' && asString !== true) {
24952             // this relies on a 'recent' version of chrome apparently...
24953             try {
24954                 var fd = (new FormData(this.el.dom)).entries();
24955                 var ret = {};
24956                 var ent = fd.next();
24957                 while (!ent.done) {
24958                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
24959                     ent = fd.next();
24960                 };
24961                 return ret;
24962             } catch(e) {
24963                 
24964             }
24965             
24966         }
24967         
24968         
24969         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24970         if(asString === true){
24971             return fs;
24972         }
24973         return Roo.urlDecode(fs);
24974     },
24975     
24976     /**
24977      * Returns the fields in this form as an object with key/value pairs. 
24978      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24979      * @return {Object}
24980      */
24981     getFieldValues : function(with_hidden)
24982     {
24983         if (this.childForms) {
24984             // copy values from the child forms
24985             // should this call getFieldValues - probably not as we do not currently copy
24986             // hidden fields when we generate..
24987             Roo.each(this.childForms, function (f) {
24988                 this.setValues(f.getValues());
24989             }, this);
24990         }
24991         
24992         var ret = {};
24993         this.items.each(function(f){
24994             if (!f.getName()) {
24995                 return;
24996             }
24997             var v = f.getValue();
24998             if (f.inputType =='radio') {
24999                 if (typeof(ret[f.getName()]) == 'undefined') {
25000                     ret[f.getName()] = ''; // empty..
25001                 }
25002                 
25003                 if (!f.el.dom.checked) {
25004                     return;
25005                     
25006                 }
25007                 v = f.el.dom.value;
25008                 
25009             }
25010             
25011             // not sure if this supported any more..
25012             if ((typeof(v) == 'object') && f.getRawValue) {
25013                 v = f.getRawValue() ; // dates..
25014             }
25015             // combo boxes where name != hiddenName...
25016             if (f.name != f.getName()) {
25017                 ret[f.name] = f.getRawValue();
25018             }
25019             ret[f.getName()] = v;
25020         });
25021         
25022         return ret;
25023     },
25024
25025     /**
25026      * Clears all invalid messages in this form.
25027      * @return {BasicForm} this
25028      */
25029     clearInvalid : function(){
25030         this.items.each(function(f){
25031            f.clearInvalid();
25032         });
25033         
25034         Roo.each(this.childForms || [], function (f) {
25035             f.clearInvalid();
25036         });
25037         
25038         
25039         return this;
25040     },
25041
25042     /**
25043      * Resets this form.
25044      * @return {BasicForm} this
25045      */
25046     reset : function(){
25047         this.items.each(function(f){
25048             f.reset();
25049         });
25050         
25051         Roo.each(this.childForms || [], function (f) {
25052             f.reset();
25053         });
25054         this.resetHasChanged();
25055         
25056         return this;
25057     },
25058
25059     /**
25060      * Add Roo.form components to this form.
25061      * @param {Field} field1
25062      * @param {Field} field2 (optional)
25063      * @param {Field} etc (optional)
25064      * @return {BasicForm} this
25065      */
25066     add : function(){
25067         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25068         return this;
25069     },
25070
25071
25072     /**
25073      * Removes a field from the items collection (does NOT remove its markup).
25074      * @param {Field} field
25075      * @return {BasicForm} this
25076      */
25077     remove : function(field){
25078         this.items.remove(field);
25079         return this;
25080     },
25081
25082     /**
25083      * Looks at the fields in this form, checks them for an id attribute,
25084      * and calls applyTo on the existing dom element with that id.
25085      * @return {BasicForm} this
25086      */
25087     render : function(){
25088         this.items.each(function(f){
25089             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25090                 f.applyTo(f.id);
25091             }
25092         });
25093         return this;
25094     },
25095
25096     /**
25097      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25098      * @param {Object} values
25099      * @return {BasicForm} this
25100      */
25101     applyToFields : function(o){
25102         this.items.each(function(f){
25103            Roo.apply(f, o);
25104         });
25105         return this;
25106     },
25107
25108     /**
25109      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25110      * @param {Object} values
25111      * @return {BasicForm} this
25112      */
25113     applyIfToFields : function(o){
25114         this.items.each(function(f){
25115            Roo.applyIf(f, o);
25116         });
25117         return this;
25118     }
25119 });
25120
25121 // back compat
25122 Roo.BasicForm = Roo.form.BasicForm;
25123
25124 Roo.apply(Roo.form.BasicForm, {
25125     
25126     popover : {
25127         
25128         padding : 5,
25129         
25130         isApplied : false,
25131         
25132         isMasked : false,
25133         
25134         form : false,
25135         
25136         target : false,
25137         
25138         intervalID : false,
25139         
25140         maskEl : false,
25141         
25142         apply : function()
25143         {
25144             if(this.isApplied){
25145                 return;
25146             }
25147             
25148             this.maskEl = {
25149                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25150                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25151                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25152                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25153             };
25154             
25155             this.maskEl.top.enableDisplayMode("block");
25156             this.maskEl.left.enableDisplayMode("block");
25157             this.maskEl.bottom.enableDisplayMode("block");
25158             this.maskEl.right.enableDisplayMode("block");
25159             
25160             Roo.get(document.body).on('click', function(){
25161                 this.unmask();
25162             }, this);
25163             
25164             Roo.get(document.body).on('touchstart', function(){
25165                 this.unmask();
25166             }, this);
25167             
25168             this.isApplied = true
25169         },
25170         
25171         mask : function(form, target)
25172         {
25173             this.form = form;
25174             
25175             this.target = target;
25176             
25177             if(!this.form.errorMask || !target.el){
25178                 return;
25179             }
25180             
25181             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25182             
25183             var ot = this.target.el.calcOffsetsTo(scrollable);
25184             
25185             var scrollTo = ot[1] - this.form.maskOffset;
25186             
25187             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25188             
25189             scrollable.scrollTo('top', scrollTo);
25190             
25191             var el = this.target.wrap || this.target.el;
25192             
25193             var box = el.getBox();
25194             
25195             this.maskEl.top.setStyle('position', 'absolute');
25196             this.maskEl.top.setStyle('z-index', 10000);
25197             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25198             this.maskEl.top.setLeft(0);
25199             this.maskEl.top.setTop(0);
25200             this.maskEl.top.show();
25201             
25202             this.maskEl.left.setStyle('position', 'absolute');
25203             this.maskEl.left.setStyle('z-index', 10000);
25204             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25205             this.maskEl.left.setLeft(0);
25206             this.maskEl.left.setTop(box.y - this.padding);
25207             this.maskEl.left.show();
25208
25209             this.maskEl.bottom.setStyle('position', 'absolute');
25210             this.maskEl.bottom.setStyle('z-index', 10000);
25211             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25212             this.maskEl.bottom.setLeft(0);
25213             this.maskEl.bottom.setTop(box.bottom + this.padding);
25214             this.maskEl.bottom.show();
25215
25216             this.maskEl.right.setStyle('position', 'absolute');
25217             this.maskEl.right.setStyle('z-index', 10000);
25218             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25219             this.maskEl.right.setLeft(box.right + this.padding);
25220             this.maskEl.right.setTop(box.y - this.padding);
25221             this.maskEl.right.show();
25222
25223             this.intervalID = window.setInterval(function() {
25224                 Roo.form.BasicForm.popover.unmask();
25225             }, 10000);
25226
25227             window.onwheel = function(){ return false;};
25228             
25229             (function(){ this.isMasked = true; }).defer(500, this);
25230             
25231         },
25232         
25233         unmask : function()
25234         {
25235             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25236                 return;
25237             }
25238             
25239             this.maskEl.top.setStyle('position', 'absolute');
25240             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25241             this.maskEl.top.hide();
25242
25243             this.maskEl.left.setStyle('position', 'absolute');
25244             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25245             this.maskEl.left.hide();
25246
25247             this.maskEl.bottom.setStyle('position', 'absolute');
25248             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25249             this.maskEl.bottom.hide();
25250
25251             this.maskEl.right.setStyle('position', 'absolute');
25252             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25253             this.maskEl.right.hide();
25254             
25255             window.onwheel = function(){ return true;};
25256             
25257             if(this.intervalID){
25258                 window.clearInterval(this.intervalID);
25259                 this.intervalID = false;
25260             }
25261             
25262             this.isMasked = false;
25263             
25264         }
25265         
25266     }
25267     
25268 });/*
25269  * Based on:
25270  * Ext JS Library 1.1.1
25271  * Copyright(c) 2006-2007, Ext JS, LLC.
25272  *
25273  * Originally Released Under LGPL - original licence link has changed is not relivant.
25274  *
25275  * Fork - LGPL
25276  * <script type="text/javascript">
25277  */
25278
25279 /**
25280  * @class Roo.form.Form
25281  * @extends Roo.form.BasicForm
25282  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25283  * @constructor
25284  * @param {Object} config Configuration options
25285  */
25286 Roo.form.Form = function(config){
25287     var xitems =  [];
25288     if (config.items) {
25289         xitems = config.items;
25290         delete config.items;
25291     }
25292    
25293     
25294     Roo.form.Form.superclass.constructor.call(this, null, config);
25295     this.url = this.url || this.action;
25296     if(!this.root){
25297         this.root = new Roo.form.Layout(Roo.applyIf({
25298             id: Roo.id()
25299         }, config));
25300     }
25301     this.active = this.root;
25302     /**
25303      * Array of all the buttons that have been added to this form via {@link addButton}
25304      * @type Array
25305      */
25306     this.buttons = [];
25307     this.allItems = [];
25308     this.addEvents({
25309         /**
25310          * @event clientvalidation
25311          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25312          * @param {Form} this
25313          * @param {Boolean} valid true if the form has passed client-side validation
25314          */
25315         clientvalidation: true,
25316         /**
25317          * @event rendered
25318          * Fires when the form is rendered
25319          * @param {Roo.form.Form} form
25320          */
25321         rendered : true
25322     });
25323     
25324     if (this.progressUrl) {
25325             // push a hidden field onto the list of fields..
25326             this.addxtype( {
25327                     xns: Roo.form, 
25328                     xtype : 'Hidden', 
25329                     name : 'UPLOAD_IDENTIFIER' 
25330             });
25331         }
25332         
25333     
25334     Roo.each(xitems, this.addxtype, this);
25335     
25336 };
25337
25338 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25339     /**
25340      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25341      */
25342     /**
25343      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25344      */
25345     /**
25346      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25347      */
25348     buttonAlign:'center',
25349
25350     /**
25351      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25352      */
25353     minButtonWidth:75,
25354
25355     /**
25356      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25357      * This property cascades to child containers if not set.
25358      */
25359     labelAlign:'left',
25360
25361     /**
25362      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25363      * fires a looping event with that state. This is required to bind buttons to the valid
25364      * state using the config value formBind:true on the button.
25365      */
25366     monitorValid : false,
25367
25368     /**
25369      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25370      */
25371     monitorPoll : 200,
25372     
25373     /**
25374      * @cfg {String} progressUrl - Url to return progress data 
25375      */
25376     
25377     progressUrl : false,
25378     /**
25379      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25380      * sending a formdata with extra parameters - eg uploaded elements.
25381      */
25382     
25383     formData : false,
25384     
25385     /**
25386      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25387      * fields are added and the column is closed. If no fields are passed the column remains open
25388      * until end() is called.
25389      * @param {Object} config The config to pass to the column
25390      * @param {Field} field1 (optional)
25391      * @param {Field} field2 (optional)
25392      * @param {Field} etc (optional)
25393      * @return Column The column container object
25394      */
25395     column : function(c){
25396         var col = new Roo.form.Column(c);
25397         this.start(col);
25398         if(arguments.length > 1){ // duplicate code required because of Opera
25399             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25400             this.end();
25401         }
25402         return col;
25403     },
25404
25405     /**
25406      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25407      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25408      * until end() is called.
25409      * @param {Object} config The config to pass to the fieldset
25410      * @param {Field} field1 (optional)
25411      * @param {Field} field2 (optional)
25412      * @param {Field} etc (optional)
25413      * @return FieldSet The fieldset container object
25414      */
25415     fieldset : function(c){
25416         var fs = new Roo.form.FieldSet(c);
25417         this.start(fs);
25418         if(arguments.length > 1){ // duplicate code required because of Opera
25419             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25420             this.end();
25421         }
25422         return fs;
25423     },
25424
25425     /**
25426      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25427      * fields are added and the container is closed. If no fields are passed the container remains open
25428      * until end() is called.
25429      * @param {Object} config The config to pass to the Layout
25430      * @param {Field} field1 (optional)
25431      * @param {Field} field2 (optional)
25432      * @param {Field} etc (optional)
25433      * @return Layout The container object
25434      */
25435     container : function(c){
25436         var l = new Roo.form.Layout(c);
25437         this.start(l);
25438         if(arguments.length > 1){ // duplicate code required because of Opera
25439             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25440             this.end();
25441         }
25442         return l;
25443     },
25444
25445     /**
25446      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25447      * @param {Object} container A Roo.form.Layout or subclass of Layout
25448      * @return {Form} this
25449      */
25450     start : function(c){
25451         // cascade label info
25452         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25453         this.active.stack.push(c);
25454         c.ownerCt = this.active;
25455         this.active = c;
25456         return this;
25457     },
25458
25459     /**
25460      * Closes the current open container
25461      * @return {Form} this
25462      */
25463     end : function(){
25464         if(this.active == this.root){
25465             return this;
25466         }
25467         this.active = this.active.ownerCt;
25468         return this;
25469     },
25470
25471     /**
25472      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25473      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25474      * as the label of the field.
25475      * @param {Field} field1
25476      * @param {Field} field2 (optional)
25477      * @param {Field} etc. (optional)
25478      * @return {Form} this
25479      */
25480     add : function(){
25481         this.active.stack.push.apply(this.active.stack, arguments);
25482         this.allItems.push.apply(this.allItems,arguments);
25483         var r = [];
25484         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25485             if(a[i].isFormField){
25486                 r.push(a[i]);
25487             }
25488         }
25489         if(r.length > 0){
25490             Roo.form.Form.superclass.add.apply(this, r);
25491         }
25492         return this;
25493     },
25494     
25495
25496     
25497     
25498     
25499      /**
25500      * Find any element that has been added to a form, using it's ID or name
25501      * This can include framesets, columns etc. along with regular fields..
25502      * @param {String} id - id or name to find.
25503      
25504      * @return {Element} e - or false if nothing found.
25505      */
25506     findbyId : function(id)
25507     {
25508         var ret = false;
25509         if (!id) {
25510             return ret;
25511         }
25512         Roo.each(this.allItems, function(f){
25513             if (f.id == id || f.name == id ){
25514                 ret = f;
25515                 return false;
25516             }
25517         });
25518         return ret;
25519     },
25520
25521     
25522     
25523     /**
25524      * Render this form into the passed container. This should only be called once!
25525      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25526      * @return {Form} this
25527      */
25528     render : function(ct)
25529     {
25530         
25531         
25532         
25533         ct = Roo.get(ct);
25534         var o = this.autoCreate || {
25535             tag: 'form',
25536             method : this.method || 'POST',
25537             id : this.id || Roo.id()
25538         };
25539         this.initEl(ct.createChild(o));
25540
25541         this.root.render(this.el);
25542         
25543        
25544              
25545         this.items.each(function(f){
25546             f.render('x-form-el-'+f.id);
25547         });
25548
25549         if(this.buttons.length > 0){
25550             // tables are required to maintain order and for correct IE layout
25551             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25552                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25553                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25554             }}, null, true);
25555             var tr = tb.getElementsByTagName('tr')[0];
25556             for(var i = 0, len = this.buttons.length; i < len; i++) {
25557                 var b = this.buttons[i];
25558                 var td = document.createElement('td');
25559                 td.className = 'x-form-btn-td';
25560                 b.render(tr.appendChild(td));
25561             }
25562         }
25563         if(this.monitorValid){ // initialize after render
25564             this.startMonitoring();
25565         }
25566         this.fireEvent('rendered', this);
25567         return this;
25568     },
25569
25570     /**
25571      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25572      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25573      * object or a valid Roo.DomHelper element config
25574      * @param {Function} handler The function called when the button is clicked
25575      * @param {Object} scope (optional) The scope of the handler function
25576      * @return {Roo.Button}
25577      */
25578     addButton : function(config, handler, scope){
25579         var bc = {
25580             handler: handler,
25581             scope: scope,
25582             minWidth: this.minButtonWidth,
25583             hideParent:true
25584         };
25585         if(typeof config == "string"){
25586             bc.text = config;
25587         }else{
25588             Roo.apply(bc, config);
25589         }
25590         var btn = new Roo.Button(null, bc);
25591         this.buttons.push(btn);
25592         return btn;
25593     },
25594
25595      /**
25596      * Adds a series of form elements (using the xtype property as the factory method.
25597      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25598      * @param {Object} config 
25599      */
25600     
25601     addxtype : function()
25602     {
25603         var ar = Array.prototype.slice.call(arguments, 0);
25604         var ret = false;
25605         for(var i = 0; i < ar.length; i++) {
25606             if (!ar[i]) {
25607                 continue; // skip -- if this happends something invalid got sent, we 
25608                 // should ignore it, as basically that interface element will not show up
25609                 // and that should be pretty obvious!!
25610             }
25611             
25612             if (Roo.form[ar[i].xtype]) {
25613                 ar[i].form = this;
25614                 var fe = Roo.factory(ar[i], Roo.form);
25615                 if (!ret) {
25616                     ret = fe;
25617                 }
25618                 fe.form = this;
25619                 if (fe.store) {
25620                     fe.store.form = this;
25621                 }
25622                 if (fe.isLayout) {  
25623                          
25624                     this.start(fe);
25625                     this.allItems.push(fe);
25626                     if (fe.items && fe.addxtype) {
25627                         fe.addxtype.apply(fe, fe.items);
25628                         delete fe.items;
25629                     }
25630                      this.end();
25631                     continue;
25632                 }
25633                 
25634                 
25635                  
25636                 this.add(fe);
25637               //  console.log('adding ' + ar[i].xtype);
25638             }
25639             if (ar[i].xtype == 'Button') {  
25640                 //console.log('adding button');
25641                 //console.log(ar[i]);
25642                 this.addButton(ar[i]);
25643                 this.allItems.push(fe);
25644                 continue;
25645             }
25646             
25647             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25648                 alert('end is not supported on xtype any more, use items');
25649             //    this.end();
25650             //    //console.log('adding end');
25651             }
25652             
25653         }
25654         return ret;
25655     },
25656     
25657     /**
25658      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25659      * option "monitorValid"
25660      */
25661     startMonitoring : function(){
25662         if(!this.bound){
25663             this.bound = true;
25664             Roo.TaskMgr.start({
25665                 run : this.bindHandler,
25666                 interval : this.monitorPoll || 200,
25667                 scope: this
25668             });
25669         }
25670     },
25671
25672     /**
25673      * Stops monitoring of the valid state of this form
25674      */
25675     stopMonitoring : function(){
25676         this.bound = false;
25677     },
25678
25679     // private
25680     bindHandler : function(){
25681         if(!this.bound){
25682             return false; // stops binding
25683         }
25684         var valid = true;
25685         this.items.each(function(f){
25686             if(!f.isValid(true)){
25687                 valid = false;
25688                 return false;
25689             }
25690         });
25691         for(var i = 0, len = this.buttons.length; i < len; i++){
25692             var btn = this.buttons[i];
25693             if(btn.formBind === true && btn.disabled === valid){
25694                 btn.setDisabled(!valid);
25695             }
25696         }
25697         this.fireEvent('clientvalidation', this, valid);
25698     }
25699     
25700     
25701     
25702     
25703     
25704     
25705     
25706     
25707 });
25708
25709
25710 // back compat
25711 Roo.Form = Roo.form.Form;
25712 /*
25713  * Based on:
25714  * Ext JS Library 1.1.1
25715  * Copyright(c) 2006-2007, Ext JS, LLC.
25716  *
25717  * Originally Released Under LGPL - original licence link has changed is not relivant.
25718  *
25719  * Fork - LGPL
25720  * <script type="text/javascript">
25721  */
25722
25723 // as we use this in bootstrap.
25724 Roo.namespace('Roo.form');
25725  /**
25726  * @class Roo.form.Action
25727  * Internal Class used to handle form actions
25728  * @constructor
25729  * @param {Roo.form.BasicForm} el The form element or its id
25730  * @param {Object} config Configuration options
25731  */
25732
25733  
25734  
25735 // define the action interface
25736 Roo.form.Action = function(form, options){
25737     this.form = form;
25738     this.options = options || {};
25739 };
25740 /**
25741  * Client Validation Failed
25742  * @const 
25743  */
25744 Roo.form.Action.CLIENT_INVALID = 'client';
25745 /**
25746  * Server Validation Failed
25747  * @const 
25748  */
25749 Roo.form.Action.SERVER_INVALID = 'server';
25750  /**
25751  * Connect to Server Failed
25752  * @const 
25753  */
25754 Roo.form.Action.CONNECT_FAILURE = 'connect';
25755 /**
25756  * Reading Data from Server Failed
25757  * @const 
25758  */
25759 Roo.form.Action.LOAD_FAILURE = 'load';
25760
25761 Roo.form.Action.prototype = {
25762     type : 'default',
25763     failureType : undefined,
25764     response : undefined,
25765     result : undefined,
25766
25767     // interface method
25768     run : function(options){
25769
25770     },
25771
25772     // interface method
25773     success : function(response){
25774
25775     },
25776
25777     // interface method
25778     handleResponse : function(response){
25779
25780     },
25781
25782     // default connection failure
25783     failure : function(response){
25784         
25785         this.response = response;
25786         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25787         this.form.afterAction(this, false);
25788     },
25789
25790     processResponse : function(response){
25791         this.response = response;
25792         if(!response.responseText){
25793             return true;
25794         }
25795         this.result = this.handleResponse(response);
25796         return this.result;
25797     },
25798
25799     // utility functions used internally
25800     getUrl : function(appendParams){
25801         var url = this.options.url || this.form.url || this.form.el.dom.action;
25802         if(appendParams){
25803             var p = this.getParams();
25804             if(p){
25805                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25806             }
25807         }
25808         return url;
25809     },
25810
25811     getMethod : function(){
25812         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25813     },
25814
25815     getParams : function(){
25816         var bp = this.form.baseParams;
25817         var p = this.options.params;
25818         if(p){
25819             if(typeof p == "object"){
25820                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25821             }else if(typeof p == 'string' && bp){
25822                 p += '&' + Roo.urlEncode(bp);
25823             }
25824         }else if(bp){
25825             p = Roo.urlEncode(bp);
25826         }
25827         return p;
25828     },
25829
25830     createCallback : function(){
25831         return {
25832             success: this.success,
25833             failure: this.failure,
25834             scope: this,
25835             timeout: (this.form.timeout*1000),
25836             upload: this.form.fileUpload ? this.success : undefined
25837         };
25838     }
25839 };
25840
25841 Roo.form.Action.Submit = function(form, options){
25842     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25843 };
25844
25845 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25846     type : 'submit',
25847
25848     haveProgress : false,
25849     uploadComplete : false,
25850     
25851     // uploadProgress indicator.
25852     uploadProgress : function()
25853     {
25854         if (!this.form.progressUrl) {
25855             return;
25856         }
25857         
25858         if (!this.haveProgress) {
25859             Roo.MessageBox.progress("Uploading", "Uploading");
25860         }
25861         if (this.uploadComplete) {
25862            Roo.MessageBox.hide();
25863            return;
25864         }
25865         
25866         this.haveProgress = true;
25867    
25868         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25869         
25870         var c = new Roo.data.Connection();
25871         c.request({
25872             url : this.form.progressUrl,
25873             params: {
25874                 id : uid
25875             },
25876             method: 'GET',
25877             success : function(req){
25878                //console.log(data);
25879                 var rdata = false;
25880                 var edata;
25881                 try  {
25882                    rdata = Roo.decode(req.responseText)
25883                 } catch (e) {
25884                     Roo.log("Invalid data from server..");
25885                     Roo.log(edata);
25886                     return;
25887                 }
25888                 if (!rdata || !rdata.success) {
25889                     Roo.log(rdata);
25890                     Roo.MessageBox.alert(Roo.encode(rdata));
25891                     return;
25892                 }
25893                 var data = rdata.data;
25894                 
25895                 if (this.uploadComplete) {
25896                    Roo.MessageBox.hide();
25897                    return;
25898                 }
25899                    
25900                 if (data){
25901                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25902                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25903                     );
25904                 }
25905                 this.uploadProgress.defer(2000,this);
25906             },
25907        
25908             failure: function(data) {
25909                 Roo.log('progress url failed ');
25910                 Roo.log(data);
25911             },
25912             scope : this
25913         });
25914            
25915     },
25916     
25917     
25918     run : function()
25919     {
25920         // run get Values on the form, so it syncs any secondary forms.
25921         this.form.getValues();
25922         
25923         var o = this.options;
25924         var method = this.getMethod();
25925         var isPost = method == 'POST';
25926         if(o.clientValidation === false || this.form.isValid()){
25927             
25928             if (this.form.progressUrl) {
25929                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25930                     (new Date() * 1) + '' + Math.random());
25931                     
25932             } 
25933             
25934             
25935             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25936                 form:this.form.el.dom,
25937                 url:this.getUrl(!isPost),
25938                 method: method,
25939                 params:isPost ? this.getParams() : null,
25940                 isUpload: this.form.fileUpload,
25941                 formData : this.form.formData
25942             }));
25943             
25944             this.uploadProgress();
25945
25946         }else if (o.clientValidation !== false){ // client validation failed
25947             this.failureType = Roo.form.Action.CLIENT_INVALID;
25948             this.form.afterAction(this, false);
25949         }
25950     },
25951
25952     success : function(response)
25953     {
25954         this.uploadComplete= true;
25955         if (this.haveProgress) {
25956             Roo.MessageBox.hide();
25957         }
25958         
25959         
25960         var result = this.processResponse(response);
25961         if(result === true || result.success){
25962             this.form.afterAction(this, true);
25963             return;
25964         }
25965         if(result.errors){
25966             this.form.markInvalid(result.errors);
25967             this.failureType = Roo.form.Action.SERVER_INVALID;
25968         }
25969         this.form.afterAction(this, false);
25970     },
25971     failure : function(response)
25972     {
25973         this.uploadComplete= true;
25974         if (this.haveProgress) {
25975             Roo.MessageBox.hide();
25976         }
25977         
25978         this.response = response;
25979         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25980         this.form.afterAction(this, false);
25981     },
25982     
25983     handleResponse : function(response){
25984         if(this.form.errorReader){
25985             var rs = this.form.errorReader.read(response);
25986             var errors = [];
25987             if(rs.records){
25988                 for(var i = 0, len = rs.records.length; i < len; i++) {
25989                     var r = rs.records[i];
25990                     errors[i] = r.data;
25991                 }
25992             }
25993             if(errors.length < 1){
25994                 errors = null;
25995             }
25996             return {
25997                 success : rs.success,
25998                 errors : errors
25999             };
26000         }
26001         var ret = false;
26002         try {
26003             ret = Roo.decode(response.responseText);
26004         } catch (e) {
26005             ret = {
26006                 success: false,
26007                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26008                 errors : []
26009             };
26010         }
26011         return ret;
26012         
26013     }
26014 });
26015
26016
26017 Roo.form.Action.Load = function(form, options){
26018     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26019     this.reader = this.form.reader;
26020 };
26021
26022 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26023     type : 'load',
26024
26025     run : function(){
26026         
26027         Roo.Ajax.request(Roo.apply(
26028                 this.createCallback(), {
26029                     method:this.getMethod(),
26030                     url:this.getUrl(false),
26031                     params:this.getParams()
26032         }));
26033     },
26034
26035     success : function(response){
26036         
26037         var result = this.processResponse(response);
26038         if(result === true || !result.success || !result.data){
26039             this.failureType = Roo.form.Action.LOAD_FAILURE;
26040             this.form.afterAction(this, false);
26041             return;
26042         }
26043         this.form.clearInvalid();
26044         this.form.setValues(result.data);
26045         this.form.afterAction(this, true);
26046     },
26047
26048     handleResponse : function(response){
26049         if(this.form.reader){
26050             var rs = this.form.reader.read(response);
26051             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26052             return {
26053                 success : rs.success,
26054                 data : data
26055             };
26056         }
26057         return Roo.decode(response.responseText);
26058     }
26059 });
26060
26061 Roo.form.Action.ACTION_TYPES = {
26062     'load' : Roo.form.Action.Load,
26063     'submit' : Roo.form.Action.Submit
26064 };/*
26065  * Based on:
26066  * Ext JS Library 1.1.1
26067  * Copyright(c) 2006-2007, Ext JS, LLC.
26068  *
26069  * Originally Released Under LGPL - original licence link has changed is not relivant.
26070  *
26071  * Fork - LGPL
26072  * <script type="text/javascript">
26073  */
26074  
26075 /**
26076  * @class Roo.form.Layout
26077  * @extends Roo.Component
26078  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26079  * @constructor
26080  * @param {Object} config Configuration options
26081  */
26082 Roo.form.Layout = function(config){
26083     var xitems = [];
26084     if (config.items) {
26085         xitems = config.items;
26086         delete config.items;
26087     }
26088     Roo.form.Layout.superclass.constructor.call(this, config);
26089     this.stack = [];
26090     Roo.each(xitems, this.addxtype, this);
26091      
26092 };
26093
26094 Roo.extend(Roo.form.Layout, Roo.Component, {
26095     /**
26096      * @cfg {String/Object} autoCreate
26097      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26098      */
26099     /**
26100      * @cfg {String/Object/Function} style
26101      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26102      * a function which returns such a specification.
26103      */
26104     /**
26105      * @cfg {String} labelAlign
26106      * Valid values are "left," "top" and "right" (defaults to "left")
26107      */
26108     /**
26109      * @cfg {Number} labelWidth
26110      * Fixed width in pixels of all field labels (defaults to undefined)
26111      */
26112     /**
26113      * @cfg {Boolean} clear
26114      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26115      */
26116     clear : true,
26117     /**
26118      * @cfg {String} labelSeparator
26119      * The separator to use after field labels (defaults to ':')
26120      */
26121     labelSeparator : ':',
26122     /**
26123      * @cfg {Boolean} hideLabels
26124      * True to suppress the display of field labels in this layout (defaults to false)
26125      */
26126     hideLabels : false,
26127
26128     // private
26129     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26130     
26131     isLayout : true,
26132     
26133     // private
26134     onRender : function(ct, position){
26135         if(this.el){ // from markup
26136             this.el = Roo.get(this.el);
26137         }else {  // generate
26138             var cfg = this.getAutoCreate();
26139             this.el = ct.createChild(cfg, position);
26140         }
26141         if(this.style){
26142             this.el.applyStyles(this.style);
26143         }
26144         if(this.labelAlign){
26145             this.el.addClass('x-form-label-'+this.labelAlign);
26146         }
26147         if(this.hideLabels){
26148             this.labelStyle = "display:none";
26149             this.elementStyle = "padding-left:0;";
26150         }else{
26151             if(typeof this.labelWidth == 'number'){
26152                 this.labelStyle = "width:"+this.labelWidth+"px;";
26153                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26154             }
26155             if(this.labelAlign == 'top'){
26156                 this.labelStyle = "width:auto;";
26157                 this.elementStyle = "padding-left:0;";
26158             }
26159         }
26160         var stack = this.stack;
26161         var slen = stack.length;
26162         if(slen > 0){
26163             if(!this.fieldTpl){
26164                 var t = new Roo.Template(
26165                     '<div class="x-form-item {5}">',
26166                         '<label for="{0}" style="{2}">{1}{4}</label>',
26167                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26168                         '</div>',
26169                     '</div><div class="x-form-clear-left"></div>'
26170                 );
26171                 t.disableFormats = true;
26172                 t.compile();
26173                 Roo.form.Layout.prototype.fieldTpl = t;
26174             }
26175             for(var i = 0; i < slen; i++) {
26176                 if(stack[i].isFormField){
26177                     this.renderField(stack[i]);
26178                 }else{
26179                     this.renderComponent(stack[i]);
26180                 }
26181             }
26182         }
26183         if(this.clear){
26184             this.el.createChild({cls:'x-form-clear'});
26185         }
26186     },
26187
26188     // private
26189     renderField : function(f){
26190         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26191                f.id, //0
26192                f.fieldLabel, //1
26193                f.labelStyle||this.labelStyle||'', //2
26194                this.elementStyle||'', //3
26195                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26196                f.itemCls||this.itemCls||''  //5
26197        ], true).getPrevSibling());
26198     },
26199
26200     // private
26201     renderComponent : function(c){
26202         c.render(c.isLayout ? this.el : this.el.createChild());    
26203     },
26204     /**
26205      * Adds a object form elements (using the xtype property as the factory method.)
26206      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26207      * @param {Object} config 
26208      */
26209     addxtype : function(o)
26210     {
26211         // create the lement.
26212         o.form = this.form;
26213         var fe = Roo.factory(o, Roo.form);
26214         this.form.allItems.push(fe);
26215         this.stack.push(fe);
26216         
26217         if (fe.isFormField) {
26218             this.form.items.add(fe);
26219         }
26220          
26221         return fe;
26222     }
26223 });
26224
26225 /**
26226  * @class Roo.form.Column
26227  * @extends Roo.form.Layout
26228  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26229  * @constructor
26230  * @param {Object} config Configuration options
26231  */
26232 Roo.form.Column = function(config){
26233     Roo.form.Column.superclass.constructor.call(this, config);
26234 };
26235
26236 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26237     /**
26238      * @cfg {Number/String} width
26239      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26240      */
26241     /**
26242      * @cfg {String/Object} autoCreate
26243      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26244      */
26245
26246     // private
26247     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26248
26249     // private
26250     onRender : function(ct, position){
26251         Roo.form.Column.superclass.onRender.call(this, ct, position);
26252         if(this.width){
26253             this.el.setWidth(this.width);
26254         }
26255     }
26256 });
26257
26258
26259 /**
26260  * @class Roo.form.Row
26261  * @extends Roo.form.Layout
26262  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26263  * @constructor
26264  * @param {Object} config Configuration options
26265  */
26266
26267  
26268 Roo.form.Row = function(config){
26269     Roo.form.Row.superclass.constructor.call(this, config);
26270 };
26271  
26272 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26273       /**
26274      * @cfg {Number/String} width
26275      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26276      */
26277     /**
26278      * @cfg {Number/String} height
26279      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26280      */
26281     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26282     
26283     padWidth : 20,
26284     // private
26285     onRender : function(ct, position){
26286         //console.log('row render');
26287         if(!this.rowTpl){
26288             var t = new Roo.Template(
26289                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26290                     '<label for="{0}" style="{2}">{1}{4}</label>',
26291                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26292                     '</div>',
26293                 '</div>'
26294             );
26295             t.disableFormats = true;
26296             t.compile();
26297             Roo.form.Layout.prototype.rowTpl = t;
26298         }
26299         this.fieldTpl = this.rowTpl;
26300         
26301         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26302         var labelWidth = 100;
26303         
26304         if ((this.labelAlign != 'top')) {
26305             if (typeof this.labelWidth == 'number') {
26306                 labelWidth = this.labelWidth
26307             }
26308             this.padWidth =  20 + labelWidth;
26309             
26310         }
26311         
26312         Roo.form.Column.superclass.onRender.call(this, ct, position);
26313         if(this.width){
26314             this.el.setWidth(this.width);
26315         }
26316         if(this.height){
26317             this.el.setHeight(this.height);
26318         }
26319     },
26320     
26321     // private
26322     renderField : function(f){
26323         f.fieldEl = this.fieldTpl.append(this.el, [
26324                f.id, f.fieldLabel,
26325                f.labelStyle||this.labelStyle||'',
26326                this.elementStyle||'',
26327                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26328                f.itemCls||this.itemCls||'',
26329                f.width ? f.width + this.padWidth : 160 + this.padWidth
26330        ],true);
26331     }
26332 });
26333  
26334
26335 /**
26336  * @class Roo.form.FieldSet
26337  * @extends Roo.form.Layout
26338  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26339  * @constructor
26340  * @param {Object} config Configuration options
26341  */
26342 Roo.form.FieldSet = function(config){
26343     Roo.form.FieldSet.superclass.constructor.call(this, config);
26344 };
26345
26346 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26347     /**
26348      * @cfg {String} legend
26349      * The text to display as the legend for the FieldSet (defaults to '')
26350      */
26351     /**
26352      * @cfg {String/Object} autoCreate
26353      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26354      */
26355
26356     // private
26357     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26358
26359     // private
26360     onRender : function(ct, position){
26361         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26362         if(this.legend){
26363             this.setLegend(this.legend);
26364         }
26365     },
26366
26367     // private
26368     setLegend : function(text){
26369         if(this.rendered){
26370             this.el.child('legend').update(text);
26371         }
26372     }
26373 });/*
26374  * Based on:
26375  * Ext JS Library 1.1.1
26376  * Copyright(c) 2006-2007, Ext JS, LLC.
26377  *
26378  * Originally Released Under LGPL - original licence link has changed is not relivant.
26379  *
26380  * Fork - LGPL
26381  * <script type="text/javascript">
26382  */
26383 /**
26384  * @class Roo.form.VTypes
26385  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26386  * @singleton
26387  */
26388 Roo.form.VTypes = function(){
26389     // closure these in so they are only created once.
26390     var alpha = /^[a-zA-Z_]+$/;
26391     var alphanum = /^[a-zA-Z0-9_]+$/;
26392     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26393     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26394
26395     // All these messages and functions are configurable
26396     return {
26397         /**
26398          * The function used to validate email addresses
26399          * @param {String} value The email address
26400          */
26401         'email' : function(v){
26402             return email.test(v);
26403         },
26404         /**
26405          * The error text to display when the email validation function returns false
26406          * @type String
26407          */
26408         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26409         /**
26410          * The keystroke filter mask to be applied on email input
26411          * @type RegExp
26412          */
26413         'emailMask' : /[a-z0-9_\.\-@]/i,
26414
26415         /**
26416          * The function used to validate URLs
26417          * @param {String} value The URL
26418          */
26419         'url' : function(v){
26420             return url.test(v);
26421         },
26422         /**
26423          * The error text to display when the url validation function returns false
26424          * @type String
26425          */
26426         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26427         
26428         /**
26429          * The function used to validate alpha values
26430          * @param {String} value The value
26431          */
26432         'alpha' : function(v){
26433             return alpha.test(v);
26434         },
26435         /**
26436          * The error text to display when the alpha validation function returns false
26437          * @type String
26438          */
26439         'alphaText' : 'This field should only contain letters and _',
26440         /**
26441          * The keystroke filter mask to be applied on alpha input
26442          * @type RegExp
26443          */
26444         'alphaMask' : /[a-z_]/i,
26445
26446         /**
26447          * The function used to validate alphanumeric values
26448          * @param {String} value The value
26449          */
26450         'alphanum' : function(v){
26451             return alphanum.test(v);
26452         },
26453         /**
26454          * The error text to display when the alphanumeric validation function returns false
26455          * @type String
26456          */
26457         'alphanumText' : 'This field should only contain letters, numbers and _',
26458         /**
26459          * The keystroke filter mask to be applied on alphanumeric input
26460          * @type RegExp
26461          */
26462         'alphanumMask' : /[a-z0-9_]/i
26463     };
26464 }();//<script type="text/javascript">
26465
26466 /**
26467  * @class Roo.form.FCKeditor
26468  * @extends Roo.form.TextArea
26469  * Wrapper around the FCKEditor http://www.fckeditor.net
26470  * @constructor
26471  * Creates a new FCKeditor
26472  * @param {Object} config Configuration options
26473  */
26474 Roo.form.FCKeditor = function(config){
26475     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26476     this.addEvents({
26477          /**
26478          * @event editorinit
26479          * Fired when the editor is initialized - you can add extra handlers here..
26480          * @param {FCKeditor} this
26481          * @param {Object} the FCK object.
26482          */
26483         editorinit : true
26484     });
26485     
26486     
26487 };
26488 Roo.form.FCKeditor.editors = { };
26489 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26490 {
26491     //defaultAutoCreate : {
26492     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26493     //},
26494     // private
26495     /**
26496      * @cfg {Object} fck options - see fck manual for details.
26497      */
26498     fckconfig : false,
26499     
26500     /**
26501      * @cfg {Object} fck toolbar set (Basic or Default)
26502      */
26503     toolbarSet : 'Basic',
26504     /**
26505      * @cfg {Object} fck BasePath
26506      */ 
26507     basePath : '/fckeditor/',
26508     
26509     
26510     frame : false,
26511     
26512     value : '',
26513     
26514    
26515     onRender : function(ct, position)
26516     {
26517         if(!this.el){
26518             this.defaultAutoCreate = {
26519                 tag: "textarea",
26520                 style:"width:300px;height:60px;",
26521                 autocomplete: "new-password"
26522             };
26523         }
26524         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26525         /*
26526         if(this.grow){
26527             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26528             if(this.preventScrollbars){
26529                 this.el.setStyle("overflow", "hidden");
26530             }
26531             this.el.setHeight(this.growMin);
26532         }
26533         */
26534         //console.log('onrender' + this.getId() );
26535         Roo.form.FCKeditor.editors[this.getId()] = this;
26536          
26537
26538         this.replaceTextarea() ;
26539         
26540     },
26541     
26542     getEditor : function() {
26543         return this.fckEditor;
26544     },
26545     /**
26546      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26547      * @param {Mixed} value The value to set
26548      */
26549     
26550     
26551     setValue : function(value)
26552     {
26553         //console.log('setValue: ' + value);
26554         
26555         if(typeof(value) == 'undefined') { // not sure why this is happending...
26556             return;
26557         }
26558         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26559         
26560         //if(!this.el || !this.getEditor()) {
26561         //    this.value = value;
26562             //this.setValue.defer(100,this,[value]);    
26563         //    return;
26564         //} 
26565         
26566         if(!this.getEditor()) {
26567             return;
26568         }
26569         
26570         this.getEditor().SetData(value);
26571         
26572         //
26573
26574     },
26575
26576     /**
26577      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26578      * @return {Mixed} value The field value
26579      */
26580     getValue : function()
26581     {
26582         
26583         if (this.frame && this.frame.dom.style.display == 'none') {
26584             return Roo.form.FCKeditor.superclass.getValue.call(this);
26585         }
26586         
26587         if(!this.el || !this.getEditor()) {
26588            
26589            // this.getValue.defer(100,this); 
26590             return this.value;
26591         }
26592        
26593         
26594         var value=this.getEditor().GetData();
26595         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26596         return Roo.form.FCKeditor.superclass.getValue.call(this);
26597         
26598
26599     },
26600
26601     /**
26602      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26603      * @return {Mixed} value The field value
26604      */
26605     getRawValue : function()
26606     {
26607         if (this.frame && this.frame.dom.style.display == 'none') {
26608             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26609         }
26610         
26611         if(!this.el || !this.getEditor()) {
26612             //this.getRawValue.defer(100,this); 
26613             return this.value;
26614             return;
26615         }
26616         
26617         
26618         
26619         var value=this.getEditor().GetData();
26620         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26621         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26622          
26623     },
26624     
26625     setSize : function(w,h) {
26626         
26627         
26628         
26629         //if (this.frame && this.frame.dom.style.display == 'none') {
26630         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26631         //    return;
26632         //}
26633         //if(!this.el || !this.getEditor()) {
26634         //    this.setSize.defer(100,this, [w,h]); 
26635         //    return;
26636         //}
26637         
26638         
26639         
26640         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26641         
26642         this.frame.dom.setAttribute('width', w);
26643         this.frame.dom.setAttribute('height', h);
26644         this.frame.setSize(w,h);
26645         
26646     },
26647     
26648     toggleSourceEdit : function(value) {
26649         
26650       
26651          
26652         this.el.dom.style.display = value ? '' : 'none';
26653         this.frame.dom.style.display = value ?  'none' : '';
26654         
26655     },
26656     
26657     
26658     focus: function(tag)
26659     {
26660         if (this.frame.dom.style.display == 'none') {
26661             return Roo.form.FCKeditor.superclass.focus.call(this);
26662         }
26663         if(!this.el || !this.getEditor()) {
26664             this.focus.defer(100,this, [tag]); 
26665             return;
26666         }
26667         
26668         
26669         
26670         
26671         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26672         this.getEditor().Focus();
26673         if (tgs.length) {
26674             if (!this.getEditor().Selection.GetSelection()) {
26675                 this.focus.defer(100,this, [tag]); 
26676                 return;
26677             }
26678             
26679             
26680             var r = this.getEditor().EditorDocument.createRange();
26681             r.setStart(tgs[0],0);
26682             r.setEnd(tgs[0],0);
26683             this.getEditor().Selection.GetSelection().removeAllRanges();
26684             this.getEditor().Selection.GetSelection().addRange(r);
26685             this.getEditor().Focus();
26686         }
26687         
26688     },
26689     
26690     
26691     
26692     replaceTextarea : function()
26693     {
26694         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26695             return ;
26696         }
26697         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26698         //{
26699             // We must check the elements firstly using the Id and then the name.
26700         var oTextarea = document.getElementById( this.getId() );
26701         
26702         var colElementsByName = document.getElementsByName( this.getId() ) ;
26703          
26704         oTextarea.style.display = 'none' ;
26705
26706         if ( oTextarea.tabIndex ) {            
26707             this.TabIndex = oTextarea.tabIndex ;
26708         }
26709         
26710         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26711         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26712         this.frame = Roo.get(this.getId() + '___Frame')
26713     },
26714     
26715     _getConfigHtml : function()
26716     {
26717         var sConfig = '' ;
26718
26719         for ( var o in this.fckconfig ) {
26720             sConfig += sConfig.length > 0  ? '&amp;' : '';
26721             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26722         }
26723
26724         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26725     },
26726     
26727     
26728     _getIFrameHtml : function()
26729     {
26730         var sFile = 'fckeditor.html' ;
26731         /* no idea what this is about..
26732         try
26733         {
26734             if ( (/fcksource=true/i).test( window.top.location.search ) )
26735                 sFile = 'fckeditor.original.html' ;
26736         }
26737         catch (e) { 
26738         */
26739
26740         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26741         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26742         
26743         
26744         var html = '<iframe id="' + this.getId() +
26745             '___Frame" src="' + sLink +
26746             '" width="' + this.width +
26747             '" height="' + this.height + '"' +
26748             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26749             ' frameborder="0" scrolling="no"></iframe>' ;
26750
26751         return html ;
26752     },
26753     
26754     _insertHtmlBefore : function( html, element )
26755     {
26756         if ( element.insertAdjacentHTML )       {
26757             // IE
26758             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26759         } else { // Gecko
26760             var oRange = document.createRange() ;
26761             oRange.setStartBefore( element ) ;
26762             var oFragment = oRange.createContextualFragment( html );
26763             element.parentNode.insertBefore( oFragment, element ) ;
26764         }
26765     }
26766     
26767     
26768   
26769     
26770     
26771     
26772     
26773
26774 });
26775
26776 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26777
26778 function FCKeditor_OnComplete(editorInstance){
26779     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26780     f.fckEditor = editorInstance;
26781     //console.log("loaded");
26782     f.fireEvent('editorinit', f, editorInstance);
26783
26784   
26785
26786  
26787
26788
26789
26790
26791
26792
26793
26794
26795
26796
26797
26798
26799
26800
26801
26802 //<script type="text/javascript">
26803 /**
26804  * @class Roo.form.GridField
26805  * @extends Roo.form.Field
26806  * Embed a grid (or editable grid into a form)
26807  * STATUS ALPHA
26808  * 
26809  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26810  * it needs 
26811  * xgrid.store = Roo.data.Store
26812  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26813  * xgrid.store.reader = Roo.data.JsonReader 
26814  * 
26815  * 
26816  * @constructor
26817  * Creates a new GridField
26818  * @param {Object} config Configuration options
26819  */
26820 Roo.form.GridField = function(config){
26821     Roo.form.GridField.superclass.constructor.call(this, config);
26822      
26823 };
26824
26825 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26826     /**
26827      * @cfg {Number} width  - used to restrict width of grid..
26828      */
26829     width : 100,
26830     /**
26831      * @cfg {Number} height - used to restrict height of grid..
26832      */
26833     height : 50,
26834      /**
26835      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26836          * 
26837          *}
26838      */
26839     xgrid : false, 
26840     /**
26841      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26842      * {tag: "input", type: "checkbox", autocomplete: "off"})
26843      */
26844    // defaultAutoCreate : { tag: 'div' },
26845     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26846     /**
26847      * @cfg {String} addTitle Text to include for adding a title.
26848      */
26849     addTitle : false,
26850     //
26851     onResize : function(){
26852         Roo.form.Field.superclass.onResize.apply(this, arguments);
26853     },
26854
26855     initEvents : function(){
26856         // Roo.form.Checkbox.superclass.initEvents.call(this);
26857         // has no events...
26858        
26859     },
26860
26861
26862     getResizeEl : function(){
26863         return this.wrap;
26864     },
26865
26866     getPositionEl : function(){
26867         return this.wrap;
26868     },
26869
26870     // private
26871     onRender : function(ct, position){
26872         
26873         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26874         var style = this.style;
26875         delete this.style;
26876         
26877         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26878         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26879         this.viewEl = this.wrap.createChild({ tag: 'div' });
26880         if (style) {
26881             this.viewEl.applyStyles(style);
26882         }
26883         if (this.width) {
26884             this.viewEl.setWidth(this.width);
26885         }
26886         if (this.height) {
26887             this.viewEl.setHeight(this.height);
26888         }
26889         //if(this.inputValue !== undefined){
26890         //this.setValue(this.value);
26891         
26892         
26893         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26894         
26895         
26896         this.grid.render();
26897         this.grid.getDataSource().on('remove', this.refreshValue, this);
26898         this.grid.getDataSource().on('update', this.refreshValue, this);
26899         this.grid.on('afteredit', this.refreshValue, this);
26900  
26901     },
26902      
26903     
26904     /**
26905      * Sets the value of the item. 
26906      * @param {String} either an object  or a string..
26907      */
26908     setValue : function(v){
26909         //this.value = v;
26910         v = v || []; // empty set..
26911         // this does not seem smart - it really only affects memoryproxy grids..
26912         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26913             var ds = this.grid.getDataSource();
26914             // assumes a json reader..
26915             var data = {}
26916             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26917             ds.loadData( data);
26918         }
26919         // clear selection so it does not get stale.
26920         if (this.grid.sm) { 
26921             this.grid.sm.clearSelections();
26922         }
26923         
26924         Roo.form.GridField.superclass.setValue.call(this, v);
26925         this.refreshValue();
26926         // should load data in the grid really....
26927     },
26928     
26929     // private
26930     refreshValue: function() {
26931          var val = [];
26932         this.grid.getDataSource().each(function(r) {
26933             val.push(r.data);
26934         });
26935         this.el.dom.value = Roo.encode(val);
26936     }
26937     
26938      
26939     
26940     
26941 });/*
26942  * Based on:
26943  * Ext JS Library 1.1.1
26944  * Copyright(c) 2006-2007, Ext JS, LLC.
26945  *
26946  * Originally Released Under LGPL - original licence link has changed is not relivant.
26947  *
26948  * Fork - LGPL
26949  * <script type="text/javascript">
26950  */
26951 /**
26952  * @class Roo.form.DisplayField
26953  * @extends Roo.form.Field
26954  * A generic Field to display non-editable data.
26955  * @cfg {Boolean} closable (true|false) default false
26956  * @constructor
26957  * Creates a new Display Field item.
26958  * @param {Object} config Configuration options
26959  */
26960 Roo.form.DisplayField = function(config){
26961     Roo.form.DisplayField.superclass.constructor.call(this, config);
26962     
26963     this.addEvents({
26964         /**
26965          * @event close
26966          * Fires after the click the close btn
26967              * @param {Roo.form.DisplayField} this
26968              */
26969         close : true
26970     });
26971 };
26972
26973 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26974     inputType:      'hidden',
26975     allowBlank:     true,
26976     readOnly:         true,
26977     
26978  
26979     /**
26980      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26981      */
26982     focusClass : undefined,
26983     /**
26984      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26985      */
26986     fieldClass: 'x-form-field',
26987     
26988      /**
26989      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
26990      */
26991     valueRenderer: undefined,
26992     
26993     width: 100,
26994     /**
26995      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26996      * {tag: "input", type: "checkbox", autocomplete: "off"})
26997      */
26998      
26999  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27000  
27001     closable : false,
27002     
27003     onResize : function(){
27004         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27005         
27006     },
27007
27008     initEvents : function(){
27009         // Roo.form.Checkbox.superclass.initEvents.call(this);
27010         // has no events...
27011         
27012         if(this.closable){
27013             this.closeEl.on('click', this.onClose, this);
27014         }
27015        
27016     },
27017
27018
27019     getResizeEl : function(){
27020         return this.wrap;
27021     },
27022
27023     getPositionEl : function(){
27024         return this.wrap;
27025     },
27026
27027     // private
27028     onRender : function(ct, position){
27029         
27030         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27031         //if(this.inputValue !== undefined){
27032         this.wrap = this.el.wrap();
27033         
27034         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27035         
27036         if(this.closable){
27037             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27038         }
27039         
27040         if (this.bodyStyle) {
27041             this.viewEl.applyStyles(this.bodyStyle);
27042         }
27043         //this.viewEl.setStyle('padding', '2px');
27044         
27045         this.setValue(this.value);
27046         
27047     },
27048 /*
27049     // private
27050     initValue : Roo.emptyFn,
27051
27052   */
27053
27054         // private
27055     onClick : function(){
27056         
27057     },
27058
27059     /**
27060      * Sets the checked state of the checkbox.
27061      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27062      */
27063     setValue : function(v){
27064         this.value = v;
27065         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27066         // this might be called before we have a dom element..
27067         if (!this.viewEl) {
27068             return;
27069         }
27070         this.viewEl.dom.innerHTML = html;
27071         Roo.form.DisplayField.superclass.setValue.call(this, v);
27072
27073     },
27074     
27075     onClose : function(e)
27076     {
27077         e.preventDefault();
27078         
27079         this.fireEvent('close', this);
27080     }
27081 });/*
27082  * 
27083  * Licence- LGPL
27084  * 
27085  */
27086
27087 /**
27088  * @class Roo.form.DayPicker
27089  * @extends Roo.form.Field
27090  * A Day picker show [M] [T] [W] ....
27091  * @constructor
27092  * Creates a new Day Picker
27093  * @param {Object} config Configuration options
27094  */
27095 Roo.form.DayPicker= function(config){
27096     Roo.form.DayPicker.superclass.constructor.call(this, config);
27097      
27098 };
27099
27100 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27101     /**
27102      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27103      */
27104     focusClass : undefined,
27105     /**
27106      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27107      */
27108     fieldClass: "x-form-field",
27109    
27110     /**
27111      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27112      * {tag: "input", type: "checkbox", autocomplete: "off"})
27113      */
27114     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27115     
27116    
27117     actionMode : 'viewEl', 
27118     //
27119     // private
27120  
27121     inputType : 'hidden',
27122     
27123      
27124     inputElement: false, // real input element?
27125     basedOn: false, // ????
27126     
27127     isFormField: true, // not sure where this is needed!!!!
27128
27129     onResize : function(){
27130         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27131         if(!this.boxLabel){
27132             this.el.alignTo(this.wrap, 'c-c');
27133         }
27134     },
27135
27136     initEvents : function(){
27137         Roo.form.Checkbox.superclass.initEvents.call(this);
27138         this.el.on("click", this.onClick,  this);
27139         this.el.on("change", this.onClick,  this);
27140     },
27141
27142
27143     getResizeEl : function(){
27144         return this.wrap;
27145     },
27146
27147     getPositionEl : function(){
27148         return this.wrap;
27149     },
27150
27151     
27152     // private
27153     onRender : function(ct, position){
27154         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27155        
27156         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27157         
27158         var r1 = '<table><tr>';
27159         var r2 = '<tr class="x-form-daypick-icons">';
27160         for (var i=0; i < 7; i++) {
27161             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27162             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27163         }
27164         
27165         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27166         viewEl.select('img').on('click', this.onClick, this);
27167         this.viewEl = viewEl;   
27168         
27169         
27170         // this will not work on Chrome!!!
27171         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27172         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27173         
27174         
27175           
27176
27177     },
27178
27179     // private
27180     initValue : Roo.emptyFn,
27181
27182     /**
27183      * Returns the checked state of the checkbox.
27184      * @return {Boolean} True if checked, else false
27185      */
27186     getValue : function(){
27187         return this.el.dom.value;
27188         
27189     },
27190
27191         // private
27192     onClick : function(e){ 
27193         //this.setChecked(!this.checked);
27194         Roo.get(e.target).toggleClass('x-menu-item-checked');
27195         this.refreshValue();
27196         //if(this.el.dom.checked != this.checked){
27197         //    this.setValue(this.el.dom.checked);
27198        // }
27199     },
27200     
27201     // private
27202     refreshValue : function()
27203     {
27204         var val = '';
27205         this.viewEl.select('img',true).each(function(e,i,n)  {
27206             val += e.is(".x-menu-item-checked") ? String(n) : '';
27207         });
27208         this.setValue(val, true);
27209     },
27210
27211     /**
27212      * Sets the checked state of the checkbox.
27213      * On is always based on a string comparison between inputValue and the param.
27214      * @param {Boolean/String} value - the value to set 
27215      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27216      */
27217     setValue : function(v,suppressEvent){
27218         if (!this.el.dom) {
27219             return;
27220         }
27221         var old = this.el.dom.value ;
27222         this.el.dom.value = v;
27223         if (suppressEvent) {
27224             return ;
27225         }
27226          
27227         // update display..
27228         this.viewEl.select('img',true).each(function(e,i,n)  {
27229             
27230             var on = e.is(".x-menu-item-checked");
27231             var newv = v.indexOf(String(n)) > -1;
27232             if (on != newv) {
27233                 e.toggleClass('x-menu-item-checked');
27234             }
27235             
27236         });
27237         
27238         
27239         this.fireEvent('change', this, v, old);
27240         
27241         
27242     },
27243    
27244     // handle setting of hidden value by some other method!!?!?
27245     setFromHidden: function()
27246     {
27247         if(!this.el){
27248             return;
27249         }
27250         //console.log("SET FROM HIDDEN");
27251         //alert('setFrom hidden');
27252         this.setValue(this.el.dom.value);
27253     },
27254     
27255     onDestroy : function()
27256     {
27257         if(this.viewEl){
27258             Roo.get(this.viewEl).remove();
27259         }
27260          
27261         Roo.form.DayPicker.superclass.onDestroy.call(this);
27262     }
27263
27264 });/*
27265  * RooJS Library 1.1.1
27266  * Copyright(c) 2008-2011  Alan Knowles
27267  *
27268  * License - LGPL
27269  */
27270  
27271
27272 /**
27273  * @class Roo.form.ComboCheck
27274  * @extends Roo.form.ComboBox
27275  * A combobox for multiple select items.
27276  *
27277  * FIXME - could do with a reset button..
27278  * 
27279  * @constructor
27280  * Create a new ComboCheck
27281  * @param {Object} config Configuration options
27282  */
27283 Roo.form.ComboCheck = function(config){
27284     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27285     // should verify some data...
27286     // like
27287     // hiddenName = required..
27288     // displayField = required
27289     // valudField == required
27290     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27291     var _t = this;
27292     Roo.each(req, function(e) {
27293         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27294             throw "Roo.form.ComboCheck : missing value for: " + e;
27295         }
27296     });
27297     
27298     
27299 };
27300
27301 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27302      
27303      
27304     editable : false,
27305      
27306     selectedClass: 'x-menu-item-checked', 
27307     
27308     // private
27309     onRender : function(ct, position){
27310         var _t = this;
27311         
27312         
27313         
27314         if(!this.tpl){
27315             var cls = 'x-combo-list';
27316
27317             
27318             this.tpl =  new Roo.Template({
27319                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27320                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27321                    '<span>{' + this.displayField + '}</span>' +
27322                     '</div>' 
27323                 
27324             });
27325         }
27326  
27327         
27328         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27329         this.view.singleSelect = false;
27330         this.view.multiSelect = true;
27331         this.view.toggleSelect = true;
27332         this.pageTb.add(new Roo.Toolbar.Fill(), {
27333             
27334             text: 'Done',
27335             handler: function()
27336             {
27337                 _t.collapse();
27338             }
27339         });
27340     },
27341     
27342     onViewOver : function(e, t){
27343         // do nothing...
27344         return;
27345         
27346     },
27347     
27348     onViewClick : function(doFocus,index){
27349         return;
27350         
27351     },
27352     select: function () {
27353         //Roo.log("SELECT CALLED");
27354     },
27355      
27356     selectByValue : function(xv, scrollIntoView){
27357         var ar = this.getValueArray();
27358         var sels = [];
27359         
27360         Roo.each(ar, function(v) {
27361             if(v === undefined || v === null){
27362                 return;
27363             }
27364             var r = this.findRecord(this.valueField, v);
27365             if(r){
27366                 sels.push(this.store.indexOf(r))
27367                 
27368             }
27369         },this);
27370         this.view.select(sels);
27371         return false;
27372     },
27373     
27374     
27375     
27376     onSelect : function(record, index){
27377        // Roo.log("onselect Called");
27378        // this is only called by the clear button now..
27379         this.view.clearSelections();
27380         this.setValue('[]');
27381         if (this.value != this.valueBefore) {
27382             this.fireEvent('change', this, this.value, this.valueBefore);
27383             this.valueBefore = this.value;
27384         }
27385     },
27386     getValueArray : function()
27387     {
27388         var ar = [] ;
27389         
27390         try {
27391             //Roo.log(this.value);
27392             if (typeof(this.value) == 'undefined') {
27393                 return [];
27394             }
27395             var ar = Roo.decode(this.value);
27396             return  ar instanceof Array ? ar : []; //?? valid?
27397             
27398         } catch(e) {
27399             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27400             return [];
27401         }
27402          
27403     },
27404     expand : function ()
27405     {
27406         
27407         Roo.form.ComboCheck.superclass.expand.call(this);
27408         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27409         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27410         
27411
27412     },
27413     
27414     collapse : function(){
27415         Roo.form.ComboCheck.superclass.collapse.call(this);
27416         var sl = this.view.getSelectedIndexes();
27417         var st = this.store;
27418         var nv = [];
27419         var tv = [];
27420         var r;
27421         Roo.each(sl, function(i) {
27422             r = st.getAt(i);
27423             nv.push(r.get(this.valueField));
27424         },this);
27425         this.setValue(Roo.encode(nv));
27426         if (this.value != this.valueBefore) {
27427
27428             this.fireEvent('change', this, this.value, this.valueBefore);
27429             this.valueBefore = this.value;
27430         }
27431         
27432     },
27433     
27434     setValue : function(v){
27435         // Roo.log(v);
27436         this.value = v;
27437         
27438         var vals = this.getValueArray();
27439         var tv = [];
27440         Roo.each(vals, function(k) {
27441             var r = this.findRecord(this.valueField, k);
27442             if(r){
27443                 tv.push(r.data[this.displayField]);
27444             }else if(this.valueNotFoundText !== undefined){
27445                 tv.push( this.valueNotFoundText );
27446             }
27447         },this);
27448        // Roo.log(tv);
27449         
27450         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27451         this.hiddenField.value = v;
27452         this.value = v;
27453     }
27454     
27455 });/*
27456  * Based on:
27457  * Ext JS Library 1.1.1
27458  * Copyright(c) 2006-2007, Ext JS, LLC.
27459  *
27460  * Originally Released Under LGPL - original licence link has changed is not relivant.
27461  *
27462  * Fork - LGPL
27463  * <script type="text/javascript">
27464  */
27465  
27466 /**
27467  * @class Roo.form.Signature
27468  * @extends Roo.form.Field
27469  * Signature field.  
27470  * @constructor
27471  * 
27472  * @param {Object} config Configuration options
27473  */
27474
27475 Roo.form.Signature = function(config){
27476     Roo.form.Signature.superclass.constructor.call(this, config);
27477     
27478     this.addEvents({// not in used??
27479          /**
27480          * @event confirm
27481          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27482              * @param {Roo.form.Signature} combo This combo box
27483              */
27484         'confirm' : true,
27485         /**
27486          * @event reset
27487          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27488              * @param {Roo.form.ComboBox} combo This combo box
27489              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27490              */
27491         'reset' : true
27492     });
27493 };
27494
27495 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27496     /**
27497      * @cfg {Object} labels Label to use when rendering a form.
27498      * defaults to 
27499      * labels : { 
27500      *      clear : "Clear",
27501      *      confirm : "Confirm"
27502      *  }
27503      */
27504     labels : { 
27505         clear : "Clear",
27506         confirm : "Confirm"
27507     },
27508     /**
27509      * @cfg {Number} width The signature panel width (defaults to 300)
27510      */
27511     width: 300,
27512     /**
27513      * @cfg {Number} height The signature panel height (defaults to 100)
27514      */
27515     height : 100,
27516     /**
27517      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27518      */
27519     allowBlank : false,
27520     
27521     //private
27522     // {Object} signPanel The signature SVG panel element (defaults to {})
27523     signPanel : {},
27524     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27525     isMouseDown : false,
27526     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27527     isConfirmed : false,
27528     // {String} signatureTmp SVG mapping string (defaults to empty string)
27529     signatureTmp : '',
27530     
27531     
27532     defaultAutoCreate : { // modified by initCompnoent..
27533         tag: "input",
27534         type:"hidden"
27535     },
27536
27537     // private
27538     onRender : function(ct, position){
27539         
27540         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27541         
27542         this.wrap = this.el.wrap({
27543             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27544         });
27545         
27546         this.createToolbar(this);
27547         this.signPanel = this.wrap.createChild({
27548                 tag: 'div',
27549                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27550             }, this.el
27551         );
27552             
27553         this.svgID = Roo.id();
27554         this.svgEl = this.signPanel.createChild({
27555               xmlns : 'http://www.w3.org/2000/svg',
27556               tag : 'svg',
27557               id : this.svgID + "-svg",
27558               width: this.width,
27559               height: this.height,
27560               viewBox: '0 0 '+this.width+' '+this.height,
27561               cn : [
27562                 {
27563                     tag: "rect",
27564                     id: this.svgID + "-svg-r",
27565                     width: this.width,
27566                     height: this.height,
27567                     fill: "#ffa"
27568                 },
27569                 {
27570                     tag: "line",
27571                     id: this.svgID + "-svg-l",
27572                     x1: "0", // start
27573                     y1: (this.height*0.8), // start set the line in 80% of height
27574                     x2: this.width, // end
27575                     y2: (this.height*0.8), // end set the line in 80% of height
27576                     'stroke': "#666",
27577                     'stroke-width': "1",
27578                     'stroke-dasharray': "3",
27579                     'shape-rendering': "crispEdges",
27580                     'pointer-events': "none"
27581                 },
27582                 {
27583                     tag: "path",
27584                     id: this.svgID + "-svg-p",
27585                     'stroke': "navy",
27586                     'stroke-width': "3",
27587                     'fill': "none",
27588                     'pointer-events': 'none'
27589                 }
27590               ]
27591         });
27592         this.createSVG();
27593         this.svgBox = this.svgEl.dom.getScreenCTM();
27594     },
27595     createSVG : function(){ 
27596         var svg = this.signPanel;
27597         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27598         var t = this;
27599
27600         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27601         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27602         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27603         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27604         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27605         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27606         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27607         
27608     },
27609     isTouchEvent : function(e){
27610         return e.type.match(/^touch/);
27611     },
27612     getCoords : function (e) {
27613         var pt    = this.svgEl.dom.createSVGPoint();
27614         pt.x = e.clientX; 
27615         pt.y = e.clientY;
27616         if (this.isTouchEvent(e)) {
27617             pt.x =  e.targetTouches[0].clientX;
27618             pt.y = e.targetTouches[0].clientY;
27619         }
27620         var a = this.svgEl.dom.getScreenCTM();
27621         var b = a.inverse();
27622         var mx = pt.matrixTransform(b);
27623         return mx.x + ',' + mx.y;
27624     },
27625     //mouse event headler 
27626     down : function (e) {
27627         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27628         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27629         
27630         this.isMouseDown = true;
27631         
27632         e.preventDefault();
27633     },
27634     move : function (e) {
27635         if (this.isMouseDown) {
27636             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27637             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27638         }
27639         
27640         e.preventDefault();
27641     },
27642     up : function (e) {
27643         this.isMouseDown = false;
27644         var sp = this.signatureTmp.split(' ');
27645         
27646         if(sp.length > 1){
27647             if(!sp[sp.length-2].match(/^L/)){
27648                 sp.pop();
27649                 sp.pop();
27650                 sp.push("");
27651                 this.signatureTmp = sp.join(" ");
27652             }
27653         }
27654         if(this.getValue() != this.signatureTmp){
27655             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27656             this.isConfirmed = false;
27657         }
27658         e.preventDefault();
27659     },
27660     
27661     /**
27662      * Protected method that will not generally be called directly. It
27663      * is called when the editor creates its toolbar. Override this method if you need to
27664      * add custom toolbar buttons.
27665      * @param {HtmlEditor} editor
27666      */
27667     createToolbar : function(editor){
27668          function btn(id, toggle, handler){
27669             var xid = fid + '-'+ id ;
27670             return {
27671                 id : xid,
27672                 cmd : id,
27673                 cls : 'x-btn-icon x-edit-'+id,
27674                 enableToggle:toggle !== false,
27675                 scope: editor, // was editor...
27676                 handler:handler||editor.relayBtnCmd,
27677                 clickEvent:'mousedown',
27678                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27679                 tabIndex:-1
27680             };
27681         }
27682         
27683         
27684         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27685         this.tb = tb;
27686         this.tb.add(
27687            {
27688                 cls : ' x-signature-btn x-signature-'+id,
27689                 scope: editor, // was editor...
27690                 handler: this.reset,
27691                 clickEvent:'mousedown',
27692                 text: this.labels.clear
27693             },
27694             {
27695                  xtype : 'Fill',
27696                  xns: Roo.Toolbar
27697             }, 
27698             {
27699                 cls : '  x-signature-btn x-signature-'+id,
27700                 scope: editor, // was editor...
27701                 handler: this.confirmHandler,
27702                 clickEvent:'mousedown',
27703                 text: this.labels.confirm
27704             }
27705         );
27706     
27707     },
27708     //public
27709     /**
27710      * when user is clicked confirm then show this image.....
27711      * 
27712      * @return {String} Image Data URI
27713      */
27714     getImageDataURI : function(){
27715         var svg = this.svgEl.dom.parentNode.innerHTML;
27716         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27717         return src; 
27718     },
27719     /**
27720      * 
27721      * @return {Boolean} this.isConfirmed
27722      */
27723     getConfirmed : function(){
27724         return this.isConfirmed;
27725     },
27726     /**
27727      * 
27728      * @return {Number} this.width
27729      */
27730     getWidth : function(){
27731         return this.width;
27732     },
27733     /**
27734      * 
27735      * @return {Number} this.height
27736      */
27737     getHeight : function(){
27738         return this.height;
27739     },
27740     // private
27741     getSignature : function(){
27742         return this.signatureTmp;
27743     },
27744     // private
27745     reset : function(){
27746         this.signatureTmp = '';
27747         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27748         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27749         this.isConfirmed = false;
27750         Roo.form.Signature.superclass.reset.call(this);
27751     },
27752     setSignature : function(s){
27753         this.signatureTmp = s;
27754         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27755         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27756         this.setValue(s);
27757         this.isConfirmed = false;
27758         Roo.form.Signature.superclass.reset.call(this);
27759     }, 
27760     test : function(){
27761 //        Roo.log(this.signPanel.dom.contentWindow.up())
27762     },
27763     //private
27764     setConfirmed : function(){
27765         
27766         
27767         
27768 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27769     },
27770     // private
27771     confirmHandler : function(){
27772         if(!this.getSignature()){
27773             return;
27774         }
27775         
27776         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27777         this.setValue(this.getSignature());
27778         this.isConfirmed = true;
27779         
27780         this.fireEvent('confirm', this);
27781     },
27782     // private
27783     // Subclasses should provide the validation implementation by overriding this
27784     validateValue : function(value){
27785         if(this.allowBlank){
27786             return true;
27787         }
27788         
27789         if(this.isConfirmed){
27790             return true;
27791         }
27792         return false;
27793     }
27794 });/*
27795  * Based on:
27796  * Ext JS Library 1.1.1
27797  * Copyright(c) 2006-2007, Ext JS, LLC.
27798  *
27799  * Originally Released Under LGPL - original licence link has changed is not relivant.
27800  *
27801  * Fork - LGPL
27802  * <script type="text/javascript">
27803  */
27804  
27805
27806 /**
27807  * @class Roo.form.ComboBox
27808  * @extends Roo.form.TriggerField
27809  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27810  * @constructor
27811  * Create a new ComboBox.
27812  * @param {Object} config Configuration options
27813  */
27814 Roo.form.Select = function(config){
27815     Roo.form.Select.superclass.constructor.call(this, config);
27816      
27817 };
27818
27819 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27820     /**
27821      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27822      */
27823     /**
27824      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27825      * rendering into an Roo.Editor, defaults to false)
27826      */
27827     /**
27828      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27829      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27830      */
27831     /**
27832      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27833      */
27834     /**
27835      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27836      * the dropdown list (defaults to undefined, with no header element)
27837      */
27838
27839      /**
27840      * @cfg {String/Roo.Template} tpl The template to use to render the output
27841      */
27842      
27843     // private
27844     defaultAutoCreate : {tag: "select"  },
27845     /**
27846      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27847      */
27848     listWidth: undefined,
27849     /**
27850      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27851      * mode = 'remote' or 'text' if mode = 'local')
27852      */
27853     displayField: undefined,
27854     /**
27855      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27856      * mode = 'remote' or 'value' if mode = 'local'). 
27857      * Note: use of a valueField requires the user make a selection
27858      * in order for a value to be mapped.
27859      */
27860     valueField: undefined,
27861     
27862     
27863     /**
27864      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27865      * field's data value (defaults to the underlying DOM element's name)
27866      */
27867     hiddenName: undefined,
27868     /**
27869      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27870      */
27871     listClass: '',
27872     /**
27873      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27874      */
27875     selectedClass: 'x-combo-selected',
27876     /**
27877      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27878      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27879      * which displays a downward arrow icon).
27880      */
27881     triggerClass : 'x-form-arrow-trigger',
27882     /**
27883      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27884      */
27885     shadow:'sides',
27886     /**
27887      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27888      * anchor positions (defaults to 'tl-bl')
27889      */
27890     listAlign: 'tl-bl?',
27891     /**
27892      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27893      */
27894     maxHeight: 300,
27895     /**
27896      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27897      * query specified by the allQuery config option (defaults to 'query')
27898      */
27899     triggerAction: 'query',
27900     /**
27901      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27902      * (defaults to 4, does not apply if editable = false)
27903      */
27904     minChars : 4,
27905     /**
27906      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27907      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27908      */
27909     typeAhead: false,
27910     /**
27911      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27912      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27913      */
27914     queryDelay: 500,
27915     /**
27916      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27917      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27918      */
27919     pageSize: 0,
27920     /**
27921      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27922      * when editable = true (defaults to false)
27923      */
27924     selectOnFocus:false,
27925     /**
27926      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27927      */
27928     queryParam: 'query',
27929     /**
27930      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27931      * when mode = 'remote' (defaults to 'Loading...')
27932      */
27933     loadingText: 'Loading...',
27934     /**
27935      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27936      */
27937     resizable: false,
27938     /**
27939      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27940      */
27941     handleHeight : 8,
27942     /**
27943      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27944      * traditional select (defaults to true)
27945      */
27946     editable: true,
27947     /**
27948      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27949      */
27950     allQuery: '',
27951     /**
27952      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27953      */
27954     mode: 'remote',
27955     /**
27956      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27957      * listWidth has a higher value)
27958      */
27959     minListWidth : 70,
27960     /**
27961      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27962      * allow the user to set arbitrary text into the field (defaults to false)
27963      */
27964     forceSelection:false,
27965     /**
27966      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27967      * if typeAhead = true (defaults to 250)
27968      */
27969     typeAheadDelay : 250,
27970     /**
27971      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27972      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27973      */
27974     valueNotFoundText : undefined,
27975     
27976     /**
27977      * @cfg {String} defaultValue The value displayed after loading the store.
27978      */
27979     defaultValue: '',
27980     
27981     /**
27982      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27983      */
27984     blockFocus : false,
27985     
27986     /**
27987      * @cfg {Boolean} disableClear Disable showing of clear button.
27988      */
27989     disableClear : false,
27990     /**
27991      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
27992      */
27993     alwaysQuery : false,
27994     
27995     //private
27996     addicon : false,
27997     editicon: false,
27998     
27999     // element that contains real text value.. (when hidden is used..)
28000      
28001     // private
28002     onRender : function(ct, position){
28003         Roo.form.Field.prototype.onRender.call(this, ct, position);
28004         
28005         if(this.store){
28006             this.store.on('beforeload', this.onBeforeLoad, this);
28007             this.store.on('load', this.onLoad, this);
28008             this.store.on('loadexception', this.onLoadException, this);
28009             this.store.load({});
28010         }
28011         
28012         
28013         
28014     },
28015
28016     // private
28017     initEvents : function(){
28018         //Roo.form.ComboBox.superclass.initEvents.call(this);
28019  
28020     },
28021
28022     onDestroy : function(){
28023        
28024         if(this.store){
28025             this.store.un('beforeload', this.onBeforeLoad, this);
28026             this.store.un('load', this.onLoad, this);
28027             this.store.un('loadexception', this.onLoadException, this);
28028         }
28029         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28030     },
28031
28032     // private
28033     fireKey : function(e){
28034         if(e.isNavKeyPress() && !this.list.isVisible()){
28035             this.fireEvent("specialkey", this, e);
28036         }
28037     },
28038
28039     // private
28040     onResize: function(w, h){
28041         
28042         return; 
28043     
28044         
28045     },
28046
28047     /**
28048      * Allow or prevent the user from directly editing the field text.  If false is passed,
28049      * the user will only be able to select from the items defined in the dropdown list.  This method
28050      * is the runtime equivalent of setting the 'editable' config option at config time.
28051      * @param {Boolean} value True to allow the user to directly edit the field text
28052      */
28053     setEditable : function(value){
28054          
28055     },
28056
28057     // private
28058     onBeforeLoad : function(){
28059         
28060         Roo.log("Select before load");
28061         return;
28062     
28063         this.innerList.update(this.loadingText ?
28064                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28065         //this.restrictHeight();
28066         this.selectedIndex = -1;
28067     },
28068
28069     // private
28070     onLoad : function(){
28071
28072     
28073         var dom = this.el.dom;
28074         dom.innerHTML = '';
28075          var od = dom.ownerDocument;
28076          
28077         if (this.emptyText) {
28078             var op = od.createElement('option');
28079             op.setAttribute('value', '');
28080             op.innerHTML = String.format('{0}', this.emptyText);
28081             dom.appendChild(op);
28082         }
28083         if(this.store.getCount() > 0){
28084            
28085             var vf = this.valueField;
28086             var df = this.displayField;
28087             this.store.data.each(function(r) {
28088                 // which colmsn to use... testing - cdoe / title..
28089                 var op = od.createElement('option');
28090                 op.setAttribute('value', r.data[vf]);
28091                 op.innerHTML = String.format('{0}', r.data[df]);
28092                 dom.appendChild(op);
28093             });
28094             if (typeof(this.defaultValue != 'undefined')) {
28095                 this.setValue(this.defaultValue);
28096             }
28097             
28098              
28099         }else{
28100             //this.onEmptyResults();
28101         }
28102         //this.el.focus();
28103     },
28104     // private
28105     onLoadException : function()
28106     {
28107         dom.innerHTML = '';
28108             
28109         Roo.log("Select on load exception");
28110         return;
28111     
28112         this.collapse();
28113         Roo.log(this.store.reader.jsonData);
28114         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28115             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28116         }
28117         
28118         
28119     },
28120     // private
28121     onTypeAhead : function(){
28122          
28123     },
28124
28125     // private
28126     onSelect : function(record, index){
28127         Roo.log('on select?');
28128         return;
28129         if(this.fireEvent('beforeselect', this, record, index) !== false){
28130             this.setFromData(index > -1 ? record.data : false);
28131             this.collapse();
28132             this.fireEvent('select', this, record, index);
28133         }
28134     },
28135
28136     /**
28137      * Returns the currently selected field value or empty string if no value is set.
28138      * @return {String} value The selected value
28139      */
28140     getValue : function(){
28141         var dom = this.el.dom;
28142         this.value = dom.options[dom.selectedIndex].value;
28143         return this.value;
28144         
28145     },
28146
28147     /**
28148      * Clears any text/value currently set in the field
28149      */
28150     clearValue : function(){
28151         this.value = '';
28152         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28153         
28154     },
28155
28156     /**
28157      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28158      * will be displayed in the field.  If the value does not match the data value of an existing item,
28159      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28160      * Otherwise the field will be blank (although the value will still be set).
28161      * @param {String} value The value to match
28162      */
28163     setValue : function(v){
28164         var d = this.el.dom;
28165         for (var i =0; i < d.options.length;i++) {
28166             if (v == d.options[i].value) {
28167                 d.selectedIndex = i;
28168                 this.value = v;
28169                 return;
28170             }
28171         }
28172         this.clearValue();
28173     },
28174     /**
28175      * @property {Object} the last set data for the element
28176      */
28177     
28178     lastData : false,
28179     /**
28180      * Sets the value of the field based on a object which is related to the record format for the store.
28181      * @param {Object} value the value to set as. or false on reset?
28182      */
28183     setFromData : function(o){
28184         Roo.log('setfrom data?');
28185          
28186         
28187         
28188     },
28189     // private
28190     reset : function(){
28191         this.clearValue();
28192     },
28193     // private
28194     findRecord : function(prop, value){
28195         
28196         return false;
28197     
28198         var record;
28199         if(this.store.getCount() > 0){
28200             this.store.each(function(r){
28201                 if(r.data[prop] == value){
28202                     record = r;
28203                     return false;
28204                 }
28205                 return true;
28206             });
28207         }
28208         return record;
28209     },
28210     
28211     getName: function()
28212     {
28213         // returns hidden if it's set..
28214         if (!this.rendered) {return ''};
28215         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28216         
28217     },
28218      
28219
28220     
28221
28222     // private
28223     onEmptyResults : function(){
28224         Roo.log('empty results');
28225         //this.collapse();
28226     },
28227
28228     /**
28229      * Returns true if the dropdown list is expanded, else false.
28230      */
28231     isExpanded : function(){
28232         return false;
28233     },
28234
28235     /**
28236      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28237      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28238      * @param {String} value The data value of the item to select
28239      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28240      * selected item if it is not currently in view (defaults to true)
28241      * @return {Boolean} True if the value matched an item in the list, else false
28242      */
28243     selectByValue : function(v, scrollIntoView){
28244         Roo.log('select By Value');
28245         return false;
28246     
28247         if(v !== undefined && v !== null){
28248             var r = this.findRecord(this.valueField || this.displayField, v);
28249             if(r){
28250                 this.select(this.store.indexOf(r), scrollIntoView);
28251                 return true;
28252             }
28253         }
28254         return false;
28255     },
28256
28257     /**
28258      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28259      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28260      * @param {Number} index The zero-based index of the list item to select
28261      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28262      * selected item if it is not currently in view (defaults to true)
28263      */
28264     select : function(index, scrollIntoView){
28265         Roo.log('select ');
28266         return  ;
28267         
28268         this.selectedIndex = index;
28269         this.view.select(index);
28270         if(scrollIntoView !== false){
28271             var el = this.view.getNode(index);
28272             if(el){
28273                 this.innerList.scrollChildIntoView(el, false);
28274             }
28275         }
28276     },
28277
28278       
28279
28280     // private
28281     validateBlur : function(){
28282         
28283         return;
28284         
28285     },
28286
28287     // private
28288     initQuery : function(){
28289         this.doQuery(this.getRawValue());
28290     },
28291
28292     // private
28293     doForce : function(){
28294         if(this.el.dom.value.length > 0){
28295             this.el.dom.value =
28296                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28297              
28298         }
28299     },
28300
28301     /**
28302      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28303      * query allowing the query action to be canceled if needed.
28304      * @param {String} query The SQL query to execute
28305      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28306      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28307      * saved in the current store (defaults to false)
28308      */
28309     doQuery : function(q, forceAll){
28310         
28311         Roo.log('doQuery?');
28312         if(q === undefined || q === null){
28313             q = '';
28314         }
28315         var qe = {
28316             query: q,
28317             forceAll: forceAll,
28318             combo: this,
28319             cancel:false
28320         };
28321         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28322             return false;
28323         }
28324         q = qe.query;
28325         forceAll = qe.forceAll;
28326         if(forceAll === true || (q.length >= this.minChars)){
28327             if(this.lastQuery != q || this.alwaysQuery){
28328                 this.lastQuery = q;
28329                 if(this.mode == 'local'){
28330                     this.selectedIndex = -1;
28331                     if(forceAll){
28332                         this.store.clearFilter();
28333                     }else{
28334                         this.store.filter(this.displayField, q);
28335                     }
28336                     this.onLoad();
28337                 }else{
28338                     this.store.baseParams[this.queryParam] = q;
28339                     this.store.load({
28340                         params: this.getParams(q)
28341                     });
28342                     this.expand();
28343                 }
28344             }else{
28345                 this.selectedIndex = -1;
28346                 this.onLoad();   
28347             }
28348         }
28349     },
28350
28351     // private
28352     getParams : function(q){
28353         var p = {};
28354         //p[this.queryParam] = q;
28355         if(this.pageSize){
28356             p.start = 0;
28357             p.limit = this.pageSize;
28358         }
28359         return p;
28360     },
28361
28362     /**
28363      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28364      */
28365     collapse : function(){
28366         
28367     },
28368
28369     // private
28370     collapseIf : function(e){
28371         
28372     },
28373
28374     /**
28375      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28376      */
28377     expand : function(){
28378         
28379     } ,
28380
28381     // private
28382      
28383
28384     /** 
28385     * @cfg {Boolean} grow 
28386     * @hide 
28387     */
28388     /** 
28389     * @cfg {Number} growMin 
28390     * @hide 
28391     */
28392     /** 
28393     * @cfg {Number} growMax 
28394     * @hide 
28395     */
28396     /**
28397      * @hide
28398      * @method autoSize
28399      */
28400     
28401     setWidth : function()
28402     {
28403         
28404     },
28405     getResizeEl : function(){
28406         return this.el;
28407     }
28408 });//<script type="text/javasscript">
28409  
28410
28411 /**
28412  * @class Roo.DDView
28413  * A DnD enabled version of Roo.View.
28414  * @param {Element/String} container The Element in which to create the View.
28415  * @param {String} tpl The template string used to create the markup for each element of the View
28416  * @param {Object} config The configuration properties. These include all the config options of
28417  * {@link Roo.View} plus some specific to this class.<br>
28418  * <p>
28419  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28420  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28421  * <p>
28422  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28423 .x-view-drag-insert-above {
28424         border-top:1px dotted #3366cc;
28425 }
28426 .x-view-drag-insert-below {
28427         border-bottom:1px dotted #3366cc;
28428 }
28429 </code></pre>
28430  * 
28431  */
28432  
28433 Roo.DDView = function(container, tpl, config) {
28434     Roo.DDView.superclass.constructor.apply(this, arguments);
28435     this.getEl().setStyle("outline", "0px none");
28436     this.getEl().unselectable();
28437     if (this.dragGroup) {
28438                 this.setDraggable(this.dragGroup.split(","));
28439     }
28440     if (this.dropGroup) {
28441                 this.setDroppable(this.dropGroup.split(","));
28442     }
28443     if (this.deletable) {
28444         this.setDeletable();
28445     }
28446     this.isDirtyFlag = false;
28447         this.addEvents({
28448                 "drop" : true
28449         });
28450 };
28451
28452 Roo.extend(Roo.DDView, Roo.View, {
28453 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28454 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28455 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28456 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28457
28458         isFormField: true,
28459
28460         reset: Roo.emptyFn,
28461         
28462         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28463
28464         validate: function() {
28465                 return true;
28466         },
28467         
28468         destroy: function() {
28469                 this.purgeListeners();
28470                 this.getEl.removeAllListeners();
28471                 this.getEl().remove();
28472                 if (this.dragZone) {
28473                         if (this.dragZone.destroy) {
28474                                 this.dragZone.destroy();
28475                         }
28476                 }
28477                 if (this.dropZone) {
28478                         if (this.dropZone.destroy) {
28479                                 this.dropZone.destroy();
28480                         }
28481                 }
28482         },
28483
28484 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28485         getName: function() {
28486                 return this.name;
28487         },
28488
28489 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28490         setValue: function(v) {
28491                 if (!this.store) {
28492                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28493                 }
28494                 var data = {};
28495                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28496                 this.store.proxy = new Roo.data.MemoryProxy(data);
28497                 this.store.load();
28498         },
28499
28500 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28501         getValue: function() {
28502                 var result = '(';
28503                 this.store.each(function(rec) {
28504                         result += rec.id + ',';
28505                 });
28506                 return result.substr(0, result.length - 1) + ')';
28507         },
28508         
28509         getIds: function() {
28510                 var i = 0, result = new Array(this.store.getCount());
28511                 this.store.each(function(rec) {
28512                         result[i++] = rec.id;
28513                 });
28514                 return result;
28515         },
28516         
28517         isDirty: function() {
28518                 return this.isDirtyFlag;
28519         },
28520
28521 /**
28522  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28523  *      whole Element becomes the target, and this causes the drop gesture to append.
28524  */
28525     getTargetFromEvent : function(e) {
28526                 var target = e.getTarget();
28527                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28528                 target = target.parentNode;
28529                 }
28530                 if (!target) {
28531                         target = this.el.dom.lastChild || this.el.dom;
28532                 }
28533                 return target;
28534     },
28535
28536 /**
28537  *      Create the drag data which consists of an object which has the property "ddel" as
28538  *      the drag proxy element. 
28539  */
28540     getDragData : function(e) {
28541         var target = this.findItemFromChild(e.getTarget());
28542                 if(target) {
28543                         this.handleSelection(e);
28544                         var selNodes = this.getSelectedNodes();
28545             var dragData = {
28546                 source: this,
28547                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28548                 nodes: selNodes,
28549                 records: []
28550                         };
28551                         var selectedIndices = this.getSelectedIndexes();
28552                         for (var i = 0; i < selectedIndices.length; i++) {
28553                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28554                         }
28555                         if (selNodes.length == 1) {
28556                                 dragData.ddel = target.cloneNode(true); // the div element
28557                         } else {
28558                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28559                                 div.className = 'multi-proxy';
28560                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28561                                         div.appendChild(selNodes[i].cloneNode(true));
28562                                 }
28563                                 dragData.ddel = div;
28564                         }
28565             //console.log(dragData)
28566             //console.log(dragData.ddel.innerHTML)
28567                         return dragData;
28568                 }
28569         //console.log('nodragData')
28570                 return false;
28571     },
28572     
28573 /**     Specify to which ddGroup items in this DDView may be dragged. */
28574     setDraggable: function(ddGroup) {
28575         if (ddGroup instanceof Array) {
28576                 Roo.each(ddGroup, this.setDraggable, this);
28577                 return;
28578         }
28579         if (this.dragZone) {
28580                 this.dragZone.addToGroup(ddGroup);
28581         } else {
28582                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28583                                 containerScroll: true,
28584                                 ddGroup: ddGroup 
28585
28586                         });
28587 //                      Draggability implies selection. DragZone's mousedown selects the element.
28588                         if (!this.multiSelect) { this.singleSelect = true; }
28589
28590 //                      Wire the DragZone's handlers up to methods in *this*
28591                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28592                 }
28593     },
28594
28595 /**     Specify from which ddGroup this DDView accepts drops. */
28596     setDroppable: function(ddGroup) {
28597         if (ddGroup instanceof Array) {
28598                 Roo.each(ddGroup, this.setDroppable, this);
28599                 return;
28600         }
28601         if (this.dropZone) {
28602                 this.dropZone.addToGroup(ddGroup);
28603         } else {
28604                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28605                                 containerScroll: true,
28606                                 ddGroup: ddGroup
28607                         });
28608
28609 //                      Wire the DropZone's handlers up to methods in *this*
28610                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28611                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28612                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28613                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28614                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28615                 }
28616     },
28617
28618 /**     Decide whether to drop above or below a View node. */
28619     getDropPoint : function(e, n, dd){
28620         if (n == this.el.dom) { return "above"; }
28621                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28622                 var c = t + (b - t) / 2;
28623                 var y = Roo.lib.Event.getPageY(e);
28624                 if(y <= c) {
28625                         return "above";
28626                 }else{
28627                         return "below";
28628                 }
28629     },
28630
28631     onNodeEnter : function(n, dd, e, data){
28632                 return false;
28633     },
28634     
28635     onNodeOver : function(n, dd, e, data){
28636                 var pt = this.getDropPoint(e, n, dd);
28637                 // set the insert point style on the target node
28638                 var dragElClass = this.dropNotAllowed;
28639                 if (pt) {
28640                         var targetElClass;
28641                         if (pt == "above"){
28642                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28643                                 targetElClass = "x-view-drag-insert-above";
28644                         } else {
28645                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28646                                 targetElClass = "x-view-drag-insert-below";
28647                         }
28648                         if (this.lastInsertClass != targetElClass){
28649                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28650                                 this.lastInsertClass = targetElClass;
28651                         }
28652                 }
28653                 return dragElClass;
28654         },
28655
28656     onNodeOut : function(n, dd, e, data){
28657                 this.removeDropIndicators(n);
28658     },
28659
28660     onNodeDrop : function(n, dd, e, data){
28661         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28662                 return false;
28663         }
28664         var pt = this.getDropPoint(e, n, dd);
28665                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28666                 if (pt == "below") { insertAt++; }
28667                 for (var i = 0; i < data.records.length; i++) {
28668                         var r = data.records[i];
28669                         var dup = this.store.getById(r.id);
28670                         if (dup && (dd != this.dragZone)) {
28671                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28672                         } else {
28673                                 if (data.copy) {
28674                                         this.store.insert(insertAt++, r.copy());
28675                                 } else {
28676                                         data.source.isDirtyFlag = true;
28677                                         r.store.remove(r);
28678                                         this.store.insert(insertAt++, r);
28679                                 }
28680                                 this.isDirtyFlag = true;
28681                         }
28682                 }
28683                 this.dragZone.cachedTarget = null;
28684                 return true;
28685     },
28686
28687     removeDropIndicators : function(n){
28688                 if(n){
28689                         Roo.fly(n).removeClass([
28690                                 "x-view-drag-insert-above",
28691                                 "x-view-drag-insert-below"]);
28692                         this.lastInsertClass = "_noclass";
28693                 }
28694     },
28695
28696 /**
28697  *      Utility method. Add a delete option to the DDView's context menu.
28698  *      @param {String} imageUrl The URL of the "delete" icon image.
28699  */
28700         setDeletable: function(imageUrl) {
28701                 if (!this.singleSelect && !this.multiSelect) {
28702                         this.singleSelect = true;
28703                 }
28704                 var c = this.getContextMenu();
28705                 this.contextMenu.on("itemclick", function(item) {
28706                         switch (item.id) {
28707                                 case "delete":
28708                                         this.remove(this.getSelectedIndexes());
28709                                         break;
28710                         }
28711                 }, this);
28712                 this.contextMenu.add({
28713                         icon: imageUrl,
28714                         id: "delete",
28715                         text: 'Delete'
28716                 });
28717         },
28718         
28719 /**     Return the context menu for this DDView. */
28720         getContextMenu: function() {
28721                 if (!this.contextMenu) {
28722 //                      Create the View's context menu
28723                         this.contextMenu = new Roo.menu.Menu({
28724                                 id: this.id + "-contextmenu"
28725                         });
28726                         this.el.on("contextmenu", this.showContextMenu, this);
28727                 }
28728                 return this.contextMenu;
28729         },
28730         
28731         disableContextMenu: function() {
28732                 if (this.contextMenu) {
28733                         this.el.un("contextmenu", this.showContextMenu, this);
28734                 }
28735         },
28736
28737         showContextMenu: function(e, item) {
28738         item = this.findItemFromChild(e.getTarget());
28739                 if (item) {
28740                         e.stopEvent();
28741                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28742                         this.contextMenu.showAt(e.getXY());
28743             }
28744     },
28745
28746 /**
28747  *      Remove {@link Roo.data.Record}s at the specified indices.
28748  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28749  */
28750     remove: function(selectedIndices) {
28751                 selectedIndices = [].concat(selectedIndices);
28752                 for (var i = 0; i < selectedIndices.length; i++) {
28753                         var rec = this.store.getAt(selectedIndices[i]);
28754                         this.store.remove(rec);
28755                 }
28756     },
28757
28758 /**
28759  *      Double click fires the event, but also, if this is draggable, and there is only one other
28760  *      related DropZone, it transfers the selected node.
28761  */
28762     onDblClick : function(e){
28763         var item = this.findItemFromChild(e.getTarget());
28764         if(item){
28765             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28766                 return false;
28767             }
28768             if (this.dragGroup) {
28769                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28770                     while (targets.indexOf(this.dropZone) > -1) {
28771                             targets.remove(this.dropZone);
28772                                 }
28773                     if (targets.length == 1) {
28774                                         this.dragZone.cachedTarget = null;
28775                         var el = Roo.get(targets[0].getEl());
28776                         var box = el.getBox(true);
28777                         targets[0].onNodeDrop(el.dom, {
28778                                 target: el.dom,
28779                                 xy: [box.x, box.y + box.height - 1]
28780                         }, null, this.getDragData(e));
28781                     }
28782                 }
28783         }
28784     },
28785     
28786     handleSelection: function(e) {
28787                 this.dragZone.cachedTarget = null;
28788         var item = this.findItemFromChild(e.getTarget());
28789         if (!item) {
28790                 this.clearSelections(true);
28791                 return;
28792         }
28793                 if (item && (this.multiSelect || this.singleSelect)){
28794                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28795                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28796                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28797                                 this.unselect(item);
28798                         } else {
28799                                 this.select(item, this.multiSelect && e.ctrlKey);
28800                                 this.lastSelection = item;
28801                         }
28802                 }
28803     },
28804
28805     onItemClick : function(item, index, e){
28806                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28807                         return false;
28808                 }
28809                 return true;
28810     },
28811
28812     unselect : function(nodeInfo, suppressEvent){
28813                 var node = this.getNode(nodeInfo);
28814                 if(node && this.isSelected(node)){
28815                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28816                                 Roo.fly(node).removeClass(this.selectedClass);
28817                                 this.selections.remove(node);
28818                                 if(!suppressEvent){
28819                                         this.fireEvent("selectionchange", this, this.selections);
28820                                 }
28821                         }
28822                 }
28823     }
28824 });
28825 /*
28826  * Based on:
28827  * Ext JS Library 1.1.1
28828  * Copyright(c) 2006-2007, Ext JS, LLC.
28829  *
28830  * Originally Released Under LGPL - original licence link has changed is not relivant.
28831  *
28832  * Fork - LGPL
28833  * <script type="text/javascript">
28834  */
28835  
28836 /**
28837  * @class Roo.LayoutManager
28838  * @extends Roo.util.Observable
28839  * Base class for layout managers.
28840  */
28841 Roo.LayoutManager = function(container, config){
28842     Roo.LayoutManager.superclass.constructor.call(this);
28843     this.el = Roo.get(container);
28844     // ie scrollbar fix
28845     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28846         document.body.scroll = "no";
28847     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28848         this.el.position('relative');
28849     }
28850     this.id = this.el.id;
28851     this.el.addClass("x-layout-container");
28852     /** false to disable window resize monitoring @type Boolean */
28853     this.monitorWindowResize = true;
28854     this.regions = {};
28855     this.addEvents({
28856         /**
28857          * @event layout
28858          * Fires when a layout is performed. 
28859          * @param {Roo.LayoutManager} this
28860          */
28861         "layout" : true,
28862         /**
28863          * @event regionresized
28864          * Fires when the user resizes a region. 
28865          * @param {Roo.LayoutRegion} region The resized region
28866          * @param {Number} newSize The new size (width for east/west, height for north/south)
28867          */
28868         "regionresized" : true,
28869         /**
28870          * @event regioncollapsed
28871          * Fires when a region is collapsed. 
28872          * @param {Roo.LayoutRegion} region The collapsed region
28873          */
28874         "regioncollapsed" : true,
28875         /**
28876          * @event regionexpanded
28877          * Fires when a region is expanded.  
28878          * @param {Roo.LayoutRegion} region The expanded region
28879          */
28880         "regionexpanded" : true
28881     });
28882     this.updating = false;
28883     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28884 };
28885
28886 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28887     /**
28888      * Returns true if this layout is currently being updated
28889      * @return {Boolean}
28890      */
28891     isUpdating : function(){
28892         return this.updating; 
28893     },
28894     
28895     /**
28896      * Suspend the LayoutManager from doing auto-layouts while
28897      * making multiple add or remove calls
28898      */
28899     beginUpdate : function(){
28900         this.updating = true;    
28901     },
28902     
28903     /**
28904      * Restore auto-layouts and optionally disable the manager from performing a layout
28905      * @param {Boolean} noLayout true to disable a layout update 
28906      */
28907     endUpdate : function(noLayout){
28908         this.updating = false;
28909         if(!noLayout){
28910             this.layout();
28911         }    
28912     },
28913     
28914     layout: function(){
28915         
28916     },
28917     
28918     onRegionResized : function(region, newSize){
28919         this.fireEvent("regionresized", region, newSize);
28920         this.layout();
28921     },
28922     
28923     onRegionCollapsed : function(region){
28924         this.fireEvent("regioncollapsed", region);
28925     },
28926     
28927     onRegionExpanded : function(region){
28928         this.fireEvent("regionexpanded", region);
28929     },
28930         
28931     /**
28932      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28933      * performs box-model adjustments.
28934      * @return {Object} The size as an object {width: (the width), height: (the height)}
28935      */
28936     getViewSize : function(){
28937         var size;
28938         if(this.el.dom != document.body){
28939             size = this.el.getSize();
28940         }else{
28941             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28942         }
28943         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28944         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28945         return size;
28946     },
28947     
28948     /**
28949      * Returns the Element this layout is bound to.
28950      * @return {Roo.Element}
28951      */
28952     getEl : function(){
28953         return this.el;
28954     },
28955     
28956     /**
28957      * Returns the specified region.
28958      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28959      * @return {Roo.LayoutRegion}
28960      */
28961     getRegion : function(target){
28962         return this.regions[target.toLowerCase()];
28963     },
28964     
28965     onWindowResize : function(){
28966         if(this.monitorWindowResize){
28967             this.layout();
28968         }
28969     }
28970 });/*
28971  * Based on:
28972  * Ext JS Library 1.1.1
28973  * Copyright(c) 2006-2007, Ext JS, LLC.
28974  *
28975  * Originally Released Under LGPL - original licence link has changed is not relivant.
28976  *
28977  * Fork - LGPL
28978  * <script type="text/javascript">
28979  */
28980 /**
28981  * @class Roo.BorderLayout
28982  * @extends Roo.LayoutManager
28983  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28984  * please see: <br><br>
28985  * <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>
28986  * <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>
28987  * Example:
28988  <pre><code>
28989  var layout = new Roo.BorderLayout(document.body, {
28990     north: {
28991         initialSize: 25,
28992         titlebar: false
28993     },
28994     west: {
28995         split:true,
28996         initialSize: 200,
28997         minSize: 175,
28998         maxSize: 400,
28999         titlebar: true,
29000         collapsible: true
29001     },
29002     east: {
29003         split:true,
29004         initialSize: 202,
29005         minSize: 175,
29006         maxSize: 400,
29007         titlebar: true,
29008         collapsible: true
29009     },
29010     south: {
29011         split:true,
29012         initialSize: 100,
29013         minSize: 100,
29014         maxSize: 200,
29015         titlebar: true,
29016         collapsible: true
29017     },
29018     center: {
29019         titlebar: true,
29020         autoScroll:true,
29021         resizeTabs: true,
29022         minTabWidth: 50,
29023         preferredTabWidth: 150
29024     }
29025 });
29026
29027 // shorthand
29028 var CP = Roo.ContentPanel;
29029
29030 layout.beginUpdate();
29031 layout.add("north", new CP("north", "North"));
29032 layout.add("south", new CP("south", {title: "South", closable: true}));
29033 layout.add("west", new CP("west", {title: "West"}));
29034 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29035 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29036 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29037 layout.getRegion("center").showPanel("center1");
29038 layout.endUpdate();
29039 </code></pre>
29040
29041 <b>The container the layout is rendered into can be either the body element or any other element.
29042 If it is not the body element, the container needs to either be an absolute positioned element,
29043 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29044 the container size if it is not the body element.</b>
29045
29046 * @constructor
29047 * Create a new BorderLayout
29048 * @param {String/HTMLElement/Element} container The container this layout is bound to
29049 * @param {Object} config Configuration options
29050  */
29051 Roo.BorderLayout = function(container, config){
29052     config = config || {};
29053     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29054     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29055     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29056         var target = this.factory.validRegions[i];
29057         if(config[target]){
29058             this.addRegion(target, config[target]);
29059         }
29060     }
29061 };
29062
29063 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29064     /**
29065      * Creates and adds a new region if it doesn't already exist.
29066      * @param {String} target The target region key (north, south, east, west or center).
29067      * @param {Object} config The regions config object
29068      * @return {BorderLayoutRegion} The new region
29069      */
29070     addRegion : function(target, config){
29071         if(!this.regions[target]){
29072             var r = this.factory.create(target, this, config);
29073             this.bindRegion(target, r);
29074         }
29075         return this.regions[target];
29076     },
29077
29078     // private (kinda)
29079     bindRegion : function(name, r){
29080         this.regions[name] = r;
29081         r.on("visibilitychange", this.layout, this);
29082         r.on("paneladded", this.layout, this);
29083         r.on("panelremoved", this.layout, this);
29084         r.on("invalidated", this.layout, this);
29085         r.on("resized", this.onRegionResized, this);
29086         r.on("collapsed", this.onRegionCollapsed, this);
29087         r.on("expanded", this.onRegionExpanded, this);
29088     },
29089
29090     /**
29091      * Performs a layout update.
29092      */
29093     layout : function(){
29094         if(this.updating) {
29095             return;
29096         }
29097         var size = this.getViewSize();
29098         var w = size.width;
29099         var h = size.height;
29100         var centerW = w;
29101         var centerH = h;
29102         var centerY = 0;
29103         var centerX = 0;
29104         //var x = 0, y = 0;
29105
29106         var rs = this.regions;
29107         var north = rs["north"];
29108         var south = rs["south"]; 
29109         var west = rs["west"];
29110         var east = rs["east"];
29111         var center = rs["center"];
29112         //if(this.hideOnLayout){ // not supported anymore
29113             //c.el.setStyle("display", "none");
29114         //}
29115         if(north && north.isVisible()){
29116             var b = north.getBox();
29117             var m = north.getMargins();
29118             b.width = w - (m.left+m.right);
29119             b.x = m.left;
29120             b.y = m.top;
29121             centerY = b.height + b.y + m.bottom;
29122             centerH -= centerY;
29123             north.updateBox(this.safeBox(b));
29124         }
29125         if(south && south.isVisible()){
29126             var b = south.getBox();
29127             var m = south.getMargins();
29128             b.width = w - (m.left+m.right);
29129             b.x = m.left;
29130             var totalHeight = (b.height + m.top + m.bottom);
29131             b.y = h - totalHeight + m.top;
29132             centerH -= totalHeight;
29133             south.updateBox(this.safeBox(b));
29134         }
29135         if(west && west.isVisible()){
29136             var b = west.getBox();
29137             var m = west.getMargins();
29138             b.height = centerH - (m.top+m.bottom);
29139             b.x = m.left;
29140             b.y = centerY + m.top;
29141             var totalWidth = (b.width + m.left + m.right);
29142             centerX += totalWidth;
29143             centerW -= totalWidth;
29144             west.updateBox(this.safeBox(b));
29145         }
29146         if(east && east.isVisible()){
29147             var b = east.getBox();
29148             var m = east.getMargins();
29149             b.height = centerH - (m.top+m.bottom);
29150             var totalWidth = (b.width + m.left + m.right);
29151             b.x = w - totalWidth + m.left;
29152             b.y = centerY + m.top;
29153             centerW -= totalWidth;
29154             east.updateBox(this.safeBox(b));
29155         }
29156         if(center){
29157             var m = center.getMargins();
29158             var centerBox = {
29159                 x: centerX + m.left,
29160                 y: centerY + m.top,
29161                 width: centerW - (m.left+m.right),
29162                 height: centerH - (m.top+m.bottom)
29163             };
29164             //if(this.hideOnLayout){
29165                 //center.el.setStyle("display", "block");
29166             //}
29167             center.updateBox(this.safeBox(centerBox));
29168         }
29169         this.el.repaint();
29170         this.fireEvent("layout", this);
29171     },
29172
29173     // private
29174     safeBox : function(box){
29175         box.width = Math.max(0, box.width);
29176         box.height = Math.max(0, box.height);
29177         return box;
29178     },
29179
29180     /**
29181      * Adds a ContentPanel (or subclass) to this layout.
29182      * @param {String} target The target region key (north, south, east, west or center).
29183      * @param {Roo.ContentPanel} panel The panel to add
29184      * @return {Roo.ContentPanel} The added panel
29185      */
29186     add : function(target, panel){
29187          
29188         target = target.toLowerCase();
29189         return this.regions[target].add(panel);
29190     },
29191
29192     /**
29193      * Remove a ContentPanel (or subclass) to this layout.
29194      * @param {String} target The target region key (north, south, east, west or center).
29195      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29196      * @return {Roo.ContentPanel} The removed panel
29197      */
29198     remove : function(target, panel){
29199         target = target.toLowerCase();
29200         return this.regions[target].remove(panel);
29201     },
29202
29203     /**
29204      * Searches all regions for a panel with the specified id
29205      * @param {String} panelId
29206      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29207      */
29208     findPanel : function(panelId){
29209         var rs = this.regions;
29210         for(var target in rs){
29211             if(typeof rs[target] != "function"){
29212                 var p = rs[target].getPanel(panelId);
29213                 if(p){
29214                     return p;
29215                 }
29216             }
29217         }
29218         return null;
29219     },
29220
29221     /**
29222      * Searches all regions for a panel with the specified id and activates (shows) it.
29223      * @param {String/ContentPanel} panelId The panels id or the panel itself
29224      * @return {Roo.ContentPanel} The shown panel or null
29225      */
29226     showPanel : function(panelId) {
29227       var rs = this.regions;
29228       for(var target in rs){
29229          var r = rs[target];
29230          if(typeof r != "function"){
29231             if(r.hasPanel(panelId)){
29232                return r.showPanel(panelId);
29233             }
29234          }
29235       }
29236       return null;
29237    },
29238
29239    /**
29240      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29241      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29242      */
29243     restoreState : function(provider){
29244         if(!provider){
29245             provider = Roo.state.Manager;
29246         }
29247         var sm = new Roo.LayoutStateManager();
29248         sm.init(this, provider);
29249     },
29250
29251     /**
29252      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29253      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29254      * a valid ContentPanel config object.  Example:
29255      * <pre><code>
29256 // Create the main layout
29257 var layout = new Roo.BorderLayout('main-ct', {
29258     west: {
29259         split:true,
29260         minSize: 175,
29261         titlebar: true
29262     },
29263     center: {
29264         title:'Components'
29265     }
29266 }, 'main-ct');
29267
29268 // Create and add multiple ContentPanels at once via configs
29269 layout.batchAdd({
29270    west: {
29271        id: 'source-files',
29272        autoCreate:true,
29273        title:'Ext Source Files',
29274        autoScroll:true,
29275        fitToFrame:true
29276    },
29277    center : {
29278        el: cview,
29279        autoScroll:true,
29280        fitToFrame:true,
29281        toolbar: tb,
29282        resizeEl:'cbody'
29283    }
29284 });
29285 </code></pre>
29286      * @param {Object} regions An object containing ContentPanel configs by region name
29287      */
29288     batchAdd : function(regions){
29289         this.beginUpdate();
29290         for(var rname in regions){
29291             var lr = this.regions[rname];
29292             if(lr){
29293                 this.addTypedPanels(lr, regions[rname]);
29294             }
29295         }
29296         this.endUpdate();
29297     },
29298
29299     // private
29300     addTypedPanels : function(lr, ps){
29301         if(typeof ps == 'string'){
29302             lr.add(new Roo.ContentPanel(ps));
29303         }
29304         else if(ps instanceof Array){
29305             for(var i =0, len = ps.length; i < len; i++){
29306                 this.addTypedPanels(lr, ps[i]);
29307             }
29308         }
29309         else if(!ps.events){ // raw config?
29310             var el = ps.el;
29311             delete ps.el; // prevent conflict
29312             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29313         }
29314         else {  // panel object assumed!
29315             lr.add(ps);
29316         }
29317     },
29318     /**
29319      * Adds a xtype elements to the layout.
29320      * <pre><code>
29321
29322 layout.addxtype({
29323        xtype : 'ContentPanel',
29324        region: 'west',
29325        items: [ .... ]
29326    }
29327 );
29328
29329 layout.addxtype({
29330         xtype : 'NestedLayoutPanel',
29331         region: 'west',
29332         layout: {
29333            center: { },
29334            west: { }   
29335         },
29336         items : [ ... list of content panels or nested layout panels.. ]
29337    }
29338 );
29339 </code></pre>
29340      * @param {Object} cfg Xtype definition of item to add.
29341      */
29342     addxtype : function(cfg)
29343     {
29344         // basically accepts a pannel...
29345         // can accept a layout region..!?!?
29346         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29347         
29348         if (!cfg.xtype.match(/Panel$/)) {
29349             return false;
29350         }
29351         var ret = false;
29352         
29353         if (typeof(cfg.region) == 'undefined') {
29354             Roo.log("Failed to add Panel, region was not set");
29355             Roo.log(cfg);
29356             return false;
29357         }
29358         var region = cfg.region;
29359         delete cfg.region;
29360         
29361           
29362         var xitems = [];
29363         if (cfg.items) {
29364             xitems = cfg.items;
29365             delete cfg.items;
29366         }
29367         var nb = false;
29368         
29369         switch(cfg.xtype) 
29370         {
29371             case 'ContentPanel':  // ContentPanel (el, cfg)
29372             case 'ScrollPanel':  // ContentPanel (el, cfg)
29373             case 'ViewPanel': 
29374                 if(cfg.autoCreate) {
29375                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29376                 } else {
29377                     var el = this.el.createChild();
29378                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29379                 }
29380                 
29381                 this.add(region, ret);
29382                 break;
29383             
29384             
29385             case 'TreePanel': // our new panel!
29386                 cfg.el = this.el.createChild();
29387                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29388                 this.add(region, ret);
29389                 break;
29390             
29391             case 'NestedLayoutPanel': 
29392                 // create a new Layout (which is  a Border Layout...
29393                 var el = this.el.createChild();
29394                 var clayout = cfg.layout;
29395                 delete cfg.layout;
29396                 clayout.items   = clayout.items  || [];
29397                 // replace this exitems with the clayout ones..
29398                 xitems = clayout.items;
29399                  
29400                 
29401                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29402                     cfg.background = false;
29403                 }
29404                 var layout = new Roo.BorderLayout(el, clayout);
29405                 
29406                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29407                 //console.log('adding nested layout panel '  + cfg.toSource());
29408                 this.add(region, ret);
29409                 nb = {}; /// find first...
29410                 break;
29411                 
29412             case 'GridPanel': 
29413             
29414                 // needs grid and region
29415                 
29416                 //var el = this.getRegion(region).el.createChild();
29417                 var el = this.el.createChild();
29418                 // create the grid first...
29419                 
29420                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29421                 delete cfg.grid;
29422                 if (region == 'center' && this.active ) {
29423                     cfg.background = false;
29424                 }
29425                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29426                 
29427                 this.add(region, ret);
29428                 if (cfg.background) {
29429                     ret.on('activate', function(gp) {
29430                         if (!gp.grid.rendered) {
29431                             gp.grid.render();
29432                         }
29433                     });
29434                 } else {
29435                     grid.render();
29436                 }
29437                 break;
29438            
29439            
29440            
29441                 
29442                 
29443                 
29444             default:
29445                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29446                     
29447                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29448                     this.add(region, ret);
29449                 } else {
29450                 
29451                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29452                     return null;
29453                 }
29454                 
29455              // GridPanel (grid, cfg)
29456             
29457         }
29458         this.beginUpdate();
29459         // add children..
29460         var region = '';
29461         var abn = {};
29462         Roo.each(xitems, function(i)  {
29463             region = nb && i.region ? i.region : false;
29464             
29465             var add = ret.addxtype(i);
29466            
29467             if (region) {
29468                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29469                 if (!i.background) {
29470                     abn[region] = nb[region] ;
29471                 }
29472             }
29473             
29474         });
29475         this.endUpdate();
29476
29477         // make the last non-background panel active..
29478         //if (nb) { Roo.log(abn); }
29479         if (nb) {
29480             
29481             for(var r in abn) {
29482                 region = this.getRegion(r);
29483                 if (region) {
29484                     // tried using nb[r], but it does not work..
29485                      
29486                     region.showPanel(abn[r]);
29487                    
29488                 }
29489             }
29490         }
29491         return ret;
29492         
29493     }
29494 });
29495
29496 /**
29497  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29498  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29499  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29500  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29501  * <pre><code>
29502 // shorthand
29503 var CP = Roo.ContentPanel;
29504
29505 var layout = Roo.BorderLayout.create({
29506     north: {
29507         initialSize: 25,
29508         titlebar: false,
29509         panels: [new CP("north", "North")]
29510     },
29511     west: {
29512         split:true,
29513         initialSize: 200,
29514         minSize: 175,
29515         maxSize: 400,
29516         titlebar: true,
29517         collapsible: true,
29518         panels: [new CP("west", {title: "West"})]
29519     },
29520     east: {
29521         split:true,
29522         initialSize: 202,
29523         minSize: 175,
29524         maxSize: 400,
29525         titlebar: true,
29526         collapsible: true,
29527         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29528     },
29529     south: {
29530         split:true,
29531         initialSize: 100,
29532         minSize: 100,
29533         maxSize: 200,
29534         titlebar: true,
29535         collapsible: true,
29536         panels: [new CP("south", {title: "South", closable: true})]
29537     },
29538     center: {
29539         titlebar: true,
29540         autoScroll:true,
29541         resizeTabs: true,
29542         minTabWidth: 50,
29543         preferredTabWidth: 150,
29544         panels: [
29545             new CP("center1", {title: "Close Me", closable: true}),
29546             new CP("center2", {title: "Center Panel", closable: false})
29547         ]
29548     }
29549 }, document.body);
29550
29551 layout.getRegion("center").showPanel("center1");
29552 </code></pre>
29553  * @param config
29554  * @param targetEl
29555  */
29556 Roo.BorderLayout.create = function(config, targetEl){
29557     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29558     layout.beginUpdate();
29559     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29560     for(var j = 0, jlen = regions.length; j < jlen; j++){
29561         var lr = regions[j];
29562         if(layout.regions[lr] && config[lr].panels){
29563             var r = layout.regions[lr];
29564             var ps = config[lr].panels;
29565             layout.addTypedPanels(r, ps);
29566         }
29567     }
29568     layout.endUpdate();
29569     return layout;
29570 };
29571
29572 // private
29573 Roo.BorderLayout.RegionFactory = {
29574     // private
29575     validRegions : ["north","south","east","west","center"],
29576
29577     // private
29578     create : function(target, mgr, config){
29579         target = target.toLowerCase();
29580         if(config.lightweight || config.basic){
29581             return new Roo.BasicLayoutRegion(mgr, config, target);
29582         }
29583         switch(target){
29584             case "north":
29585                 return new Roo.NorthLayoutRegion(mgr, config);
29586             case "south":
29587                 return new Roo.SouthLayoutRegion(mgr, config);
29588             case "east":
29589                 return new Roo.EastLayoutRegion(mgr, config);
29590             case "west":
29591                 return new Roo.WestLayoutRegion(mgr, config);
29592             case "center":
29593                 return new Roo.CenterLayoutRegion(mgr, config);
29594         }
29595         throw 'Layout region "'+target+'" not supported.';
29596     }
29597 };/*
29598  * Based on:
29599  * Ext JS Library 1.1.1
29600  * Copyright(c) 2006-2007, Ext JS, LLC.
29601  *
29602  * Originally Released Under LGPL - original licence link has changed is not relivant.
29603  *
29604  * Fork - LGPL
29605  * <script type="text/javascript">
29606  */
29607  
29608 /**
29609  * @class Roo.BasicLayoutRegion
29610  * @extends Roo.util.Observable
29611  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29612  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29613  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29614  */
29615 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29616     this.mgr = mgr;
29617     this.position  = pos;
29618     this.events = {
29619         /**
29620          * @scope Roo.BasicLayoutRegion
29621          */
29622         
29623         /**
29624          * @event beforeremove
29625          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29626          * @param {Roo.LayoutRegion} this
29627          * @param {Roo.ContentPanel} panel The panel
29628          * @param {Object} e The cancel event object
29629          */
29630         "beforeremove" : true,
29631         /**
29632          * @event invalidated
29633          * Fires when the layout for this region is changed.
29634          * @param {Roo.LayoutRegion} this
29635          */
29636         "invalidated" : true,
29637         /**
29638          * @event visibilitychange
29639          * Fires when this region is shown or hidden 
29640          * @param {Roo.LayoutRegion} this
29641          * @param {Boolean} visibility true or false
29642          */
29643         "visibilitychange" : true,
29644         /**
29645          * @event paneladded
29646          * Fires when a panel is added. 
29647          * @param {Roo.LayoutRegion} this
29648          * @param {Roo.ContentPanel} panel The panel
29649          */
29650         "paneladded" : true,
29651         /**
29652          * @event panelremoved
29653          * Fires when a panel is removed. 
29654          * @param {Roo.LayoutRegion} this
29655          * @param {Roo.ContentPanel} panel The panel
29656          */
29657         "panelremoved" : true,
29658         /**
29659          * @event beforecollapse
29660          * Fires when this region before collapse.
29661          * @param {Roo.LayoutRegion} this
29662          */
29663         "beforecollapse" : true,
29664         /**
29665          * @event collapsed
29666          * Fires when this region is collapsed.
29667          * @param {Roo.LayoutRegion} this
29668          */
29669         "collapsed" : true,
29670         /**
29671          * @event expanded
29672          * Fires when this region is expanded.
29673          * @param {Roo.LayoutRegion} this
29674          */
29675         "expanded" : true,
29676         /**
29677          * @event slideshow
29678          * Fires when this region is slid into view.
29679          * @param {Roo.LayoutRegion} this
29680          */
29681         "slideshow" : true,
29682         /**
29683          * @event slidehide
29684          * Fires when this region slides out of view. 
29685          * @param {Roo.LayoutRegion} this
29686          */
29687         "slidehide" : true,
29688         /**
29689          * @event panelactivated
29690          * Fires when a panel is activated. 
29691          * @param {Roo.LayoutRegion} this
29692          * @param {Roo.ContentPanel} panel The activated panel
29693          */
29694         "panelactivated" : true,
29695         /**
29696          * @event resized
29697          * Fires when the user resizes this region. 
29698          * @param {Roo.LayoutRegion} this
29699          * @param {Number} newSize The new size (width for east/west, height for north/south)
29700          */
29701         "resized" : true
29702     };
29703     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29704     this.panels = new Roo.util.MixedCollection();
29705     this.panels.getKey = this.getPanelId.createDelegate(this);
29706     this.box = null;
29707     this.activePanel = null;
29708     // ensure listeners are added...
29709     
29710     if (config.listeners || config.events) {
29711         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29712             listeners : config.listeners || {},
29713             events : config.events || {}
29714         });
29715     }
29716     
29717     if(skipConfig !== true){
29718         this.applyConfig(config);
29719     }
29720 };
29721
29722 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29723     getPanelId : function(p){
29724         return p.getId();
29725     },
29726     
29727     applyConfig : function(config){
29728         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29729         this.config = config;
29730         
29731     },
29732     
29733     /**
29734      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29735      * the width, for horizontal (north, south) the height.
29736      * @param {Number} newSize The new width or height
29737      */
29738     resizeTo : function(newSize){
29739         var el = this.el ? this.el :
29740                  (this.activePanel ? this.activePanel.getEl() : null);
29741         if(el){
29742             switch(this.position){
29743                 case "east":
29744                 case "west":
29745                     el.setWidth(newSize);
29746                     this.fireEvent("resized", this, newSize);
29747                 break;
29748                 case "north":
29749                 case "south":
29750                     el.setHeight(newSize);
29751                     this.fireEvent("resized", this, newSize);
29752                 break;                
29753             }
29754         }
29755     },
29756     
29757     getBox : function(){
29758         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29759     },
29760     
29761     getMargins : function(){
29762         return this.margins;
29763     },
29764     
29765     updateBox : function(box){
29766         this.box = box;
29767         var el = this.activePanel.getEl();
29768         el.dom.style.left = box.x + "px";
29769         el.dom.style.top = box.y + "px";
29770         this.activePanel.setSize(box.width, box.height);
29771     },
29772     
29773     /**
29774      * Returns the container element for this region.
29775      * @return {Roo.Element}
29776      */
29777     getEl : function(){
29778         return this.activePanel;
29779     },
29780     
29781     /**
29782      * Returns true if this region is currently visible.
29783      * @return {Boolean}
29784      */
29785     isVisible : function(){
29786         return this.activePanel ? true : false;
29787     },
29788     
29789     setActivePanel : function(panel){
29790         panel = this.getPanel(panel);
29791         if(this.activePanel && this.activePanel != panel){
29792             this.activePanel.setActiveState(false);
29793             this.activePanel.getEl().setLeftTop(-10000,-10000);
29794         }
29795         this.activePanel = panel;
29796         panel.setActiveState(true);
29797         if(this.box){
29798             panel.setSize(this.box.width, this.box.height);
29799         }
29800         this.fireEvent("panelactivated", this, panel);
29801         this.fireEvent("invalidated");
29802     },
29803     
29804     /**
29805      * Show the specified panel.
29806      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29807      * @return {Roo.ContentPanel} The shown panel or null
29808      */
29809     showPanel : function(panel){
29810         if(panel = this.getPanel(panel)){
29811             this.setActivePanel(panel);
29812         }
29813         return panel;
29814     },
29815     
29816     /**
29817      * Get the active panel for this region.
29818      * @return {Roo.ContentPanel} The active panel or null
29819      */
29820     getActivePanel : function(){
29821         return this.activePanel;
29822     },
29823     
29824     /**
29825      * Add the passed ContentPanel(s)
29826      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29827      * @return {Roo.ContentPanel} The panel added (if only one was added)
29828      */
29829     add : function(panel){
29830         if(arguments.length > 1){
29831             for(var i = 0, len = arguments.length; i < len; i++) {
29832                 this.add(arguments[i]);
29833             }
29834             return null;
29835         }
29836         if(this.hasPanel(panel)){
29837             this.showPanel(panel);
29838             return panel;
29839         }
29840         var el = panel.getEl();
29841         if(el.dom.parentNode != this.mgr.el.dom){
29842             this.mgr.el.dom.appendChild(el.dom);
29843         }
29844         if(panel.setRegion){
29845             panel.setRegion(this);
29846         }
29847         this.panels.add(panel);
29848         el.setStyle("position", "absolute");
29849         if(!panel.background){
29850             this.setActivePanel(panel);
29851             if(this.config.initialSize && this.panels.getCount()==1){
29852                 this.resizeTo(this.config.initialSize);
29853             }
29854         }
29855         this.fireEvent("paneladded", this, panel);
29856         return panel;
29857     },
29858     
29859     /**
29860      * Returns true if the panel is in this region.
29861      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29862      * @return {Boolean}
29863      */
29864     hasPanel : function(panel){
29865         if(typeof panel == "object"){ // must be panel obj
29866             panel = panel.getId();
29867         }
29868         return this.getPanel(panel) ? true : false;
29869     },
29870     
29871     /**
29872      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29873      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29874      * @param {Boolean} preservePanel Overrides the config preservePanel option
29875      * @return {Roo.ContentPanel} The panel that was removed
29876      */
29877     remove : function(panel, preservePanel){
29878         panel = this.getPanel(panel);
29879         if(!panel){
29880             return null;
29881         }
29882         var e = {};
29883         this.fireEvent("beforeremove", this, panel, e);
29884         if(e.cancel === true){
29885             return null;
29886         }
29887         var panelId = panel.getId();
29888         this.panels.removeKey(panelId);
29889         return panel;
29890     },
29891     
29892     /**
29893      * Returns the panel specified or null if it's not in this region.
29894      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29895      * @return {Roo.ContentPanel}
29896      */
29897     getPanel : function(id){
29898         if(typeof id == "object"){ // must be panel obj
29899             return id;
29900         }
29901         return this.panels.get(id);
29902     },
29903     
29904     /**
29905      * Returns this regions position (north/south/east/west/center).
29906      * @return {String} 
29907      */
29908     getPosition: function(){
29909         return this.position;    
29910     }
29911 });/*
29912  * Based on:
29913  * Ext JS Library 1.1.1
29914  * Copyright(c) 2006-2007, Ext JS, LLC.
29915  *
29916  * Originally Released Under LGPL - original licence link has changed is not relivant.
29917  *
29918  * Fork - LGPL
29919  * <script type="text/javascript">
29920  */
29921  
29922 /**
29923  * @class Roo.LayoutRegion
29924  * @extends Roo.BasicLayoutRegion
29925  * This class represents a region in a layout manager.
29926  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29927  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29928  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29929  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29930  * @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})
29931  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29932  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29933  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29934  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29935  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29936  * @cfg {String}    title           The title for the region (overrides panel titles)
29937  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29938  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29939  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29940  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29941  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29942  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29943  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29944  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29945  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29946  * @cfg {Boolean}   showPin         True to show a pin button
29947  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29948  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29949  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29950  * @cfg {Number}    width           For East/West panels
29951  * @cfg {Number}    height          For North/South panels
29952  * @cfg {Boolean}   split           To show the splitter
29953  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29954  */
29955 Roo.LayoutRegion = function(mgr, config, pos){
29956     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29957     var dh = Roo.DomHelper;
29958     /** This region's container element 
29959     * @type Roo.Element */
29960     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29961     /** This region's title element 
29962     * @type Roo.Element */
29963
29964     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29965         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29966         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29967     ]}, true);
29968     this.titleEl.enableDisplayMode();
29969     /** This region's title text element 
29970     * @type HTMLElement */
29971     this.titleTextEl = this.titleEl.dom.firstChild;
29972     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29973     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29974     this.closeBtn.enableDisplayMode();
29975     this.closeBtn.on("click", this.closeClicked, this);
29976     this.closeBtn.hide();
29977
29978     this.createBody(config);
29979     this.visible = true;
29980     this.collapsed = false;
29981
29982     if(config.hideWhenEmpty){
29983         this.hide();
29984         this.on("paneladded", this.validateVisibility, this);
29985         this.on("panelremoved", this.validateVisibility, this);
29986     }
29987     this.applyConfig(config);
29988 };
29989
29990 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
29991
29992     createBody : function(){
29993         /** This region's body element 
29994         * @type Roo.Element */
29995         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
29996     },
29997
29998     applyConfig : function(c){
29999         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30000             var dh = Roo.DomHelper;
30001             if(c.titlebar !== false){
30002                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30003                 this.collapseBtn.on("click", this.collapse, this);
30004                 this.collapseBtn.enableDisplayMode();
30005
30006                 if(c.showPin === true || this.showPin){
30007                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30008                     this.stickBtn.enableDisplayMode();
30009                     this.stickBtn.on("click", this.expand, this);
30010                     this.stickBtn.hide();
30011                 }
30012             }
30013             /** This region's collapsed element
30014             * @type Roo.Element */
30015             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30016                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30017             ]}, true);
30018             if(c.floatable !== false){
30019                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30020                this.collapsedEl.on("click", this.collapseClick, this);
30021             }
30022
30023             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30024                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30025                    id: "message", unselectable: "on", style:{"float":"left"}});
30026                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30027              }
30028             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30029             this.expandBtn.on("click", this.expand, this);
30030         }
30031         if(this.collapseBtn){
30032             this.collapseBtn.setVisible(c.collapsible == true);
30033         }
30034         this.cmargins = c.cmargins || this.cmargins ||
30035                          (this.position == "west" || this.position == "east" ?
30036                              {top: 0, left: 2, right:2, bottom: 0} :
30037                              {top: 2, left: 0, right:0, bottom: 2});
30038         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30039         this.bottomTabs = c.tabPosition != "top";
30040         this.autoScroll = c.autoScroll || false;
30041         if(this.autoScroll){
30042             this.bodyEl.setStyle("overflow", "auto");
30043         }else{
30044             this.bodyEl.setStyle("overflow", "hidden");
30045         }
30046         //if(c.titlebar !== false){
30047             if((!c.titlebar && !c.title) || c.titlebar === false){
30048                 this.titleEl.hide();
30049             }else{
30050                 this.titleEl.show();
30051                 if(c.title){
30052                     this.titleTextEl.innerHTML = c.title;
30053                 }
30054             }
30055         //}
30056         this.duration = c.duration || .30;
30057         this.slideDuration = c.slideDuration || .45;
30058         this.config = c;
30059         if(c.collapsed){
30060             this.collapse(true);
30061         }
30062         if(c.hidden){
30063             this.hide();
30064         }
30065     },
30066     /**
30067      * Returns true if this region is currently visible.
30068      * @return {Boolean}
30069      */
30070     isVisible : function(){
30071         return this.visible;
30072     },
30073
30074     /**
30075      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30076      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30077      */
30078     setCollapsedTitle : function(title){
30079         title = title || "&#160;";
30080         if(this.collapsedTitleTextEl){
30081             this.collapsedTitleTextEl.innerHTML = title;
30082         }
30083     },
30084
30085     getBox : function(){
30086         var b;
30087         if(!this.collapsed){
30088             b = this.el.getBox(false, true);
30089         }else{
30090             b = this.collapsedEl.getBox(false, true);
30091         }
30092         return b;
30093     },
30094
30095     getMargins : function(){
30096         return this.collapsed ? this.cmargins : this.margins;
30097     },
30098
30099     highlight : function(){
30100         this.el.addClass("x-layout-panel-dragover");
30101     },
30102
30103     unhighlight : function(){
30104         this.el.removeClass("x-layout-panel-dragover");
30105     },
30106
30107     updateBox : function(box){
30108         this.box = box;
30109         if(!this.collapsed){
30110             this.el.dom.style.left = box.x + "px";
30111             this.el.dom.style.top = box.y + "px";
30112             this.updateBody(box.width, box.height);
30113         }else{
30114             this.collapsedEl.dom.style.left = box.x + "px";
30115             this.collapsedEl.dom.style.top = box.y + "px";
30116             this.collapsedEl.setSize(box.width, box.height);
30117         }
30118         if(this.tabs){
30119             this.tabs.autoSizeTabs();
30120         }
30121     },
30122
30123     updateBody : function(w, h){
30124         if(w !== null){
30125             this.el.setWidth(w);
30126             w -= this.el.getBorderWidth("rl");
30127             if(this.config.adjustments){
30128                 w += this.config.adjustments[0];
30129             }
30130         }
30131         if(h !== null){
30132             this.el.setHeight(h);
30133             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30134             h -= this.el.getBorderWidth("tb");
30135             if(this.config.adjustments){
30136                 h += this.config.adjustments[1];
30137             }
30138             this.bodyEl.setHeight(h);
30139             if(this.tabs){
30140                 h = this.tabs.syncHeight(h);
30141             }
30142         }
30143         if(this.panelSize){
30144             w = w !== null ? w : this.panelSize.width;
30145             h = h !== null ? h : this.panelSize.height;
30146         }
30147         if(this.activePanel){
30148             var el = this.activePanel.getEl();
30149             w = w !== null ? w : el.getWidth();
30150             h = h !== null ? h : el.getHeight();
30151             this.panelSize = {width: w, height: h};
30152             this.activePanel.setSize(w, h);
30153         }
30154         if(Roo.isIE && this.tabs){
30155             this.tabs.el.repaint();
30156         }
30157     },
30158
30159     /**
30160      * Returns the container element for this region.
30161      * @return {Roo.Element}
30162      */
30163     getEl : function(){
30164         return this.el;
30165     },
30166
30167     /**
30168      * Hides this region.
30169      */
30170     hide : function(){
30171         if(!this.collapsed){
30172             this.el.dom.style.left = "-2000px";
30173             this.el.hide();
30174         }else{
30175             this.collapsedEl.dom.style.left = "-2000px";
30176             this.collapsedEl.hide();
30177         }
30178         this.visible = false;
30179         this.fireEvent("visibilitychange", this, false);
30180     },
30181
30182     /**
30183      * Shows this region if it was previously hidden.
30184      */
30185     show : function(){
30186         if(!this.collapsed){
30187             this.el.show();
30188         }else{
30189             this.collapsedEl.show();
30190         }
30191         this.visible = true;
30192         this.fireEvent("visibilitychange", this, true);
30193     },
30194
30195     closeClicked : function(){
30196         if(this.activePanel){
30197             this.remove(this.activePanel);
30198         }
30199     },
30200
30201     collapseClick : function(e){
30202         if(this.isSlid){
30203            e.stopPropagation();
30204            this.slideIn();
30205         }else{
30206            e.stopPropagation();
30207            this.slideOut();
30208         }
30209     },
30210
30211     /**
30212      * Collapses this region.
30213      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30214      */
30215     collapse : function(skipAnim, skipCheck){
30216         if(this.collapsed) {
30217             return;
30218         }
30219         
30220         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30221             
30222             this.collapsed = true;
30223             if(this.split){
30224                 this.split.el.hide();
30225             }
30226             if(this.config.animate && skipAnim !== true){
30227                 this.fireEvent("invalidated", this);
30228                 this.animateCollapse();
30229             }else{
30230                 this.el.setLocation(-20000,-20000);
30231                 this.el.hide();
30232                 this.collapsedEl.show();
30233                 this.fireEvent("collapsed", this);
30234                 this.fireEvent("invalidated", this);
30235             }
30236         }
30237         
30238     },
30239
30240     animateCollapse : function(){
30241         // overridden
30242     },
30243
30244     /**
30245      * Expands this region if it was previously collapsed.
30246      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30247      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30248      */
30249     expand : function(e, skipAnim){
30250         if(e) {
30251             e.stopPropagation();
30252         }
30253         if(!this.collapsed || this.el.hasActiveFx()) {
30254             return;
30255         }
30256         if(this.isSlid){
30257             this.afterSlideIn();
30258             skipAnim = true;
30259         }
30260         this.collapsed = false;
30261         if(this.config.animate && skipAnim !== true){
30262             this.animateExpand();
30263         }else{
30264             this.el.show();
30265             if(this.split){
30266                 this.split.el.show();
30267             }
30268             this.collapsedEl.setLocation(-2000,-2000);
30269             this.collapsedEl.hide();
30270             this.fireEvent("invalidated", this);
30271             this.fireEvent("expanded", this);
30272         }
30273     },
30274
30275     animateExpand : function(){
30276         // overridden
30277     },
30278
30279     initTabs : function()
30280     {
30281         this.bodyEl.setStyle("overflow", "hidden");
30282         var ts = new Roo.TabPanel(
30283                 this.bodyEl.dom,
30284                 {
30285                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30286                     disableTooltips: this.config.disableTabTips,
30287                     toolbar : this.config.toolbar
30288                 }
30289         );
30290         if(this.config.hideTabs){
30291             ts.stripWrap.setDisplayed(false);
30292         }
30293         this.tabs = ts;
30294         ts.resizeTabs = this.config.resizeTabs === true;
30295         ts.minTabWidth = this.config.minTabWidth || 40;
30296         ts.maxTabWidth = this.config.maxTabWidth || 250;
30297         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30298         ts.monitorResize = false;
30299         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30300         ts.bodyEl.addClass('x-layout-tabs-body');
30301         this.panels.each(this.initPanelAsTab, this);
30302     },
30303
30304     initPanelAsTab : function(panel){
30305         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30306                     this.config.closeOnTab && panel.isClosable());
30307         if(panel.tabTip !== undefined){
30308             ti.setTooltip(panel.tabTip);
30309         }
30310         ti.on("activate", function(){
30311               this.setActivePanel(panel);
30312         }, this);
30313         if(this.config.closeOnTab){
30314             ti.on("beforeclose", function(t, e){
30315                 e.cancel = true;
30316                 this.remove(panel);
30317             }, this);
30318         }
30319         return ti;
30320     },
30321
30322     updatePanelTitle : function(panel, title){
30323         if(this.activePanel == panel){
30324             this.updateTitle(title);
30325         }
30326         if(this.tabs){
30327             var ti = this.tabs.getTab(panel.getEl().id);
30328             ti.setText(title);
30329             if(panel.tabTip !== undefined){
30330                 ti.setTooltip(panel.tabTip);
30331             }
30332         }
30333     },
30334
30335     updateTitle : function(title){
30336         if(this.titleTextEl && !this.config.title){
30337             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30338         }
30339     },
30340
30341     setActivePanel : function(panel){
30342         panel = this.getPanel(panel);
30343         if(this.activePanel && this.activePanel != panel){
30344             this.activePanel.setActiveState(false);
30345         }
30346         this.activePanel = panel;
30347         panel.setActiveState(true);
30348         if(this.panelSize){
30349             panel.setSize(this.panelSize.width, this.panelSize.height);
30350         }
30351         if(this.closeBtn){
30352             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30353         }
30354         this.updateTitle(panel.getTitle());
30355         if(this.tabs){
30356             this.fireEvent("invalidated", this);
30357         }
30358         this.fireEvent("panelactivated", this, panel);
30359     },
30360
30361     /**
30362      * Shows the specified panel.
30363      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30364      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30365      */
30366     showPanel : function(panel)
30367     {
30368         panel = this.getPanel(panel);
30369         if(panel){
30370             if(this.tabs){
30371                 var tab = this.tabs.getTab(panel.getEl().id);
30372                 if(tab.isHidden()){
30373                     this.tabs.unhideTab(tab.id);
30374                 }
30375                 tab.activate();
30376             }else{
30377                 this.setActivePanel(panel);
30378             }
30379         }
30380         return panel;
30381     },
30382
30383     /**
30384      * Get the active panel for this region.
30385      * @return {Roo.ContentPanel} The active panel or null
30386      */
30387     getActivePanel : function(){
30388         return this.activePanel;
30389     },
30390
30391     validateVisibility : function(){
30392         if(this.panels.getCount() < 1){
30393             this.updateTitle("&#160;");
30394             this.closeBtn.hide();
30395             this.hide();
30396         }else{
30397             if(!this.isVisible()){
30398                 this.show();
30399             }
30400         }
30401     },
30402
30403     /**
30404      * Adds the passed ContentPanel(s) to this region.
30405      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30406      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30407      */
30408     add : function(panel){
30409         if(arguments.length > 1){
30410             for(var i = 0, len = arguments.length; i < len; i++) {
30411                 this.add(arguments[i]);
30412             }
30413             return null;
30414         }
30415         if(this.hasPanel(panel)){
30416             this.showPanel(panel);
30417             return panel;
30418         }
30419         panel.setRegion(this);
30420         this.panels.add(panel);
30421         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30422             this.bodyEl.dom.appendChild(panel.getEl().dom);
30423             if(panel.background !== true){
30424                 this.setActivePanel(panel);
30425             }
30426             this.fireEvent("paneladded", this, panel);
30427             return panel;
30428         }
30429         if(!this.tabs){
30430             this.initTabs();
30431         }else{
30432             this.initPanelAsTab(panel);
30433         }
30434         if(panel.background !== true){
30435             this.tabs.activate(panel.getEl().id);
30436         }
30437         this.fireEvent("paneladded", this, panel);
30438         return panel;
30439     },
30440
30441     /**
30442      * Hides the tab for the specified panel.
30443      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30444      */
30445     hidePanel : function(panel){
30446         if(this.tabs && (panel = this.getPanel(panel))){
30447             this.tabs.hideTab(panel.getEl().id);
30448         }
30449     },
30450
30451     /**
30452      * Unhides the tab for a previously hidden panel.
30453      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30454      */
30455     unhidePanel : function(panel){
30456         if(this.tabs && (panel = this.getPanel(panel))){
30457             this.tabs.unhideTab(panel.getEl().id);
30458         }
30459     },
30460
30461     clearPanels : function(){
30462         while(this.panels.getCount() > 0){
30463              this.remove(this.panels.first());
30464         }
30465     },
30466
30467     /**
30468      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30469      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30470      * @param {Boolean} preservePanel Overrides the config preservePanel option
30471      * @return {Roo.ContentPanel} The panel that was removed
30472      */
30473     remove : function(panel, preservePanel){
30474         panel = this.getPanel(panel);
30475         if(!panel){
30476             return null;
30477         }
30478         var e = {};
30479         this.fireEvent("beforeremove", this, panel, e);
30480         if(e.cancel === true){
30481             return null;
30482         }
30483         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30484         var panelId = panel.getId();
30485         this.panels.removeKey(panelId);
30486         if(preservePanel){
30487             document.body.appendChild(panel.getEl().dom);
30488         }
30489         if(this.tabs){
30490             this.tabs.removeTab(panel.getEl().id);
30491         }else if (!preservePanel){
30492             this.bodyEl.dom.removeChild(panel.getEl().dom);
30493         }
30494         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30495             var p = this.panels.first();
30496             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30497             tempEl.appendChild(p.getEl().dom);
30498             this.bodyEl.update("");
30499             this.bodyEl.dom.appendChild(p.getEl().dom);
30500             tempEl = null;
30501             this.updateTitle(p.getTitle());
30502             this.tabs = null;
30503             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30504             this.setActivePanel(p);
30505         }
30506         panel.setRegion(null);
30507         if(this.activePanel == panel){
30508             this.activePanel = null;
30509         }
30510         if(this.config.autoDestroy !== false && preservePanel !== true){
30511             try{panel.destroy();}catch(e){}
30512         }
30513         this.fireEvent("panelremoved", this, panel);
30514         return panel;
30515     },
30516
30517     /**
30518      * Returns the TabPanel component used by this region
30519      * @return {Roo.TabPanel}
30520      */
30521     getTabs : function(){
30522         return this.tabs;
30523     },
30524
30525     createTool : function(parentEl, className){
30526         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30527             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30528         btn.addClassOnOver("x-layout-tools-button-over");
30529         return btn;
30530     }
30531 });/*
30532  * Based on:
30533  * Ext JS Library 1.1.1
30534  * Copyright(c) 2006-2007, Ext JS, LLC.
30535  *
30536  * Originally Released Under LGPL - original licence link has changed is not relivant.
30537  *
30538  * Fork - LGPL
30539  * <script type="text/javascript">
30540  */
30541  
30542
30543
30544 /**
30545  * @class Roo.SplitLayoutRegion
30546  * @extends Roo.LayoutRegion
30547  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30548  */
30549 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30550     this.cursor = cursor;
30551     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30552 };
30553
30554 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30555     splitTip : "Drag to resize.",
30556     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30557     useSplitTips : false,
30558
30559     applyConfig : function(config){
30560         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30561         if(config.split){
30562             if(!this.split){
30563                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30564                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30565                 /** The SplitBar for this region 
30566                 * @type Roo.SplitBar */
30567                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30568                 this.split.on("moved", this.onSplitMove, this);
30569                 this.split.useShim = config.useShim === true;
30570                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30571                 if(this.useSplitTips){
30572                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30573                 }
30574                 if(config.collapsible){
30575                     this.split.el.on("dblclick", this.collapse,  this);
30576                 }
30577             }
30578             if(typeof config.minSize != "undefined"){
30579                 this.split.minSize = config.minSize;
30580             }
30581             if(typeof config.maxSize != "undefined"){
30582                 this.split.maxSize = config.maxSize;
30583             }
30584             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30585                 this.hideSplitter();
30586             }
30587         }
30588     },
30589
30590     getHMaxSize : function(){
30591          var cmax = this.config.maxSize || 10000;
30592          var center = this.mgr.getRegion("center");
30593          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30594     },
30595
30596     getVMaxSize : function(){
30597          var cmax = this.config.maxSize || 10000;
30598          var center = this.mgr.getRegion("center");
30599          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30600     },
30601
30602     onSplitMove : function(split, newSize){
30603         this.fireEvent("resized", this, newSize);
30604     },
30605     
30606     /** 
30607      * Returns the {@link Roo.SplitBar} for this region.
30608      * @return {Roo.SplitBar}
30609      */
30610     getSplitBar : function(){
30611         return this.split;
30612     },
30613     
30614     hide : function(){
30615         this.hideSplitter();
30616         Roo.SplitLayoutRegion.superclass.hide.call(this);
30617     },
30618
30619     hideSplitter : function(){
30620         if(this.split){
30621             this.split.el.setLocation(-2000,-2000);
30622             this.split.el.hide();
30623         }
30624     },
30625
30626     show : function(){
30627         if(this.split){
30628             this.split.el.show();
30629         }
30630         Roo.SplitLayoutRegion.superclass.show.call(this);
30631     },
30632     
30633     beforeSlide: function(){
30634         if(Roo.isGecko){// firefox overflow auto bug workaround
30635             this.bodyEl.clip();
30636             if(this.tabs) {
30637                 this.tabs.bodyEl.clip();
30638             }
30639             if(this.activePanel){
30640                 this.activePanel.getEl().clip();
30641                 
30642                 if(this.activePanel.beforeSlide){
30643                     this.activePanel.beforeSlide();
30644                 }
30645             }
30646         }
30647     },
30648     
30649     afterSlide : function(){
30650         if(Roo.isGecko){// firefox overflow auto bug workaround
30651             this.bodyEl.unclip();
30652             if(this.tabs) {
30653                 this.tabs.bodyEl.unclip();
30654             }
30655             if(this.activePanel){
30656                 this.activePanel.getEl().unclip();
30657                 if(this.activePanel.afterSlide){
30658                     this.activePanel.afterSlide();
30659                 }
30660             }
30661         }
30662     },
30663
30664     initAutoHide : function(){
30665         if(this.autoHide !== false){
30666             if(!this.autoHideHd){
30667                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30668                 this.autoHideHd = {
30669                     "mouseout": function(e){
30670                         if(!e.within(this.el, true)){
30671                             st.delay(500);
30672                         }
30673                     },
30674                     "mouseover" : function(e){
30675                         st.cancel();
30676                     },
30677                     scope : this
30678                 };
30679             }
30680             this.el.on(this.autoHideHd);
30681         }
30682     },
30683
30684     clearAutoHide : function(){
30685         if(this.autoHide !== false){
30686             this.el.un("mouseout", this.autoHideHd.mouseout);
30687             this.el.un("mouseover", this.autoHideHd.mouseover);
30688         }
30689     },
30690
30691     clearMonitor : function(){
30692         Roo.get(document).un("click", this.slideInIf, this);
30693     },
30694
30695     // these names are backwards but not changed for compat
30696     slideOut : function(){
30697         if(this.isSlid || this.el.hasActiveFx()){
30698             return;
30699         }
30700         this.isSlid = true;
30701         if(this.collapseBtn){
30702             this.collapseBtn.hide();
30703         }
30704         this.closeBtnState = this.closeBtn.getStyle('display');
30705         this.closeBtn.hide();
30706         if(this.stickBtn){
30707             this.stickBtn.show();
30708         }
30709         this.el.show();
30710         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30711         this.beforeSlide();
30712         this.el.setStyle("z-index", 10001);
30713         this.el.slideIn(this.getSlideAnchor(), {
30714             callback: function(){
30715                 this.afterSlide();
30716                 this.initAutoHide();
30717                 Roo.get(document).on("click", this.slideInIf, this);
30718                 this.fireEvent("slideshow", this);
30719             },
30720             scope: this,
30721             block: true
30722         });
30723     },
30724
30725     afterSlideIn : function(){
30726         this.clearAutoHide();
30727         this.isSlid = false;
30728         this.clearMonitor();
30729         this.el.setStyle("z-index", "");
30730         if(this.collapseBtn){
30731             this.collapseBtn.show();
30732         }
30733         this.closeBtn.setStyle('display', this.closeBtnState);
30734         if(this.stickBtn){
30735             this.stickBtn.hide();
30736         }
30737         this.fireEvent("slidehide", this);
30738     },
30739
30740     slideIn : function(cb){
30741         if(!this.isSlid || this.el.hasActiveFx()){
30742             Roo.callback(cb);
30743             return;
30744         }
30745         this.isSlid = false;
30746         this.beforeSlide();
30747         this.el.slideOut(this.getSlideAnchor(), {
30748             callback: function(){
30749                 this.el.setLeftTop(-10000, -10000);
30750                 this.afterSlide();
30751                 this.afterSlideIn();
30752                 Roo.callback(cb);
30753             },
30754             scope: this,
30755             block: true
30756         });
30757     },
30758     
30759     slideInIf : function(e){
30760         if(!e.within(this.el)){
30761             this.slideIn();
30762         }
30763     },
30764
30765     animateCollapse : function(){
30766         this.beforeSlide();
30767         this.el.setStyle("z-index", 20000);
30768         var anchor = this.getSlideAnchor();
30769         this.el.slideOut(anchor, {
30770             callback : function(){
30771                 this.el.setStyle("z-index", "");
30772                 this.collapsedEl.slideIn(anchor, {duration:.3});
30773                 this.afterSlide();
30774                 this.el.setLocation(-10000,-10000);
30775                 this.el.hide();
30776                 this.fireEvent("collapsed", this);
30777             },
30778             scope: this,
30779             block: true
30780         });
30781     },
30782
30783     animateExpand : function(){
30784         this.beforeSlide();
30785         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30786         this.el.setStyle("z-index", 20000);
30787         this.collapsedEl.hide({
30788             duration:.1
30789         });
30790         this.el.slideIn(this.getSlideAnchor(), {
30791             callback : function(){
30792                 this.el.setStyle("z-index", "");
30793                 this.afterSlide();
30794                 if(this.split){
30795                     this.split.el.show();
30796                 }
30797                 this.fireEvent("invalidated", this);
30798                 this.fireEvent("expanded", this);
30799             },
30800             scope: this,
30801             block: true
30802         });
30803     },
30804
30805     anchors : {
30806         "west" : "left",
30807         "east" : "right",
30808         "north" : "top",
30809         "south" : "bottom"
30810     },
30811
30812     sanchors : {
30813         "west" : "l",
30814         "east" : "r",
30815         "north" : "t",
30816         "south" : "b"
30817     },
30818
30819     canchors : {
30820         "west" : "tl-tr",
30821         "east" : "tr-tl",
30822         "north" : "tl-bl",
30823         "south" : "bl-tl"
30824     },
30825
30826     getAnchor : function(){
30827         return this.anchors[this.position];
30828     },
30829
30830     getCollapseAnchor : function(){
30831         return this.canchors[this.position];
30832     },
30833
30834     getSlideAnchor : function(){
30835         return this.sanchors[this.position];
30836     },
30837
30838     getAlignAdj : function(){
30839         var cm = this.cmargins;
30840         switch(this.position){
30841             case "west":
30842                 return [0, 0];
30843             break;
30844             case "east":
30845                 return [0, 0];
30846             break;
30847             case "north":
30848                 return [0, 0];
30849             break;
30850             case "south":
30851                 return [0, 0];
30852             break;
30853         }
30854     },
30855
30856     getExpandAdj : function(){
30857         var c = this.collapsedEl, cm = this.cmargins;
30858         switch(this.position){
30859             case "west":
30860                 return [-(cm.right+c.getWidth()+cm.left), 0];
30861             break;
30862             case "east":
30863                 return [cm.right+c.getWidth()+cm.left, 0];
30864             break;
30865             case "north":
30866                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30867             break;
30868             case "south":
30869                 return [0, cm.top+cm.bottom+c.getHeight()];
30870             break;
30871         }
30872     }
30873 });/*
30874  * Based on:
30875  * Ext JS Library 1.1.1
30876  * Copyright(c) 2006-2007, Ext JS, LLC.
30877  *
30878  * Originally Released Under LGPL - original licence link has changed is not relivant.
30879  *
30880  * Fork - LGPL
30881  * <script type="text/javascript">
30882  */
30883 /*
30884  * These classes are private internal classes
30885  */
30886 Roo.CenterLayoutRegion = function(mgr, config){
30887     Roo.LayoutRegion.call(this, mgr, config, "center");
30888     this.visible = true;
30889     this.minWidth = config.minWidth || 20;
30890     this.minHeight = config.minHeight || 20;
30891 };
30892
30893 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30894     hide : function(){
30895         // center panel can't be hidden
30896     },
30897     
30898     show : function(){
30899         // center panel can't be hidden
30900     },
30901     
30902     getMinWidth: function(){
30903         return this.minWidth;
30904     },
30905     
30906     getMinHeight: function(){
30907         return this.minHeight;
30908     }
30909 });
30910
30911
30912 Roo.NorthLayoutRegion = function(mgr, config){
30913     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30914     if(this.split){
30915         this.split.placement = Roo.SplitBar.TOP;
30916         this.split.orientation = Roo.SplitBar.VERTICAL;
30917         this.split.el.addClass("x-layout-split-v");
30918     }
30919     var size = config.initialSize || config.height;
30920     if(typeof size != "undefined"){
30921         this.el.setHeight(size);
30922     }
30923 };
30924 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30925     orientation: Roo.SplitBar.VERTICAL,
30926     getBox : function(){
30927         if(this.collapsed){
30928             return this.collapsedEl.getBox();
30929         }
30930         var box = this.el.getBox();
30931         if(this.split){
30932             box.height += this.split.el.getHeight();
30933         }
30934         return box;
30935     },
30936     
30937     updateBox : function(box){
30938         if(this.split && !this.collapsed){
30939             box.height -= this.split.el.getHeight();
30940             this.split.el.setLeft(box.x);
30941             this.split.el.setTop(box.y+box.height);
30942             this.split.el.setWidth(box.width);
30943         }
30944         if(this.collapsed){
30945             this.updateBody(box.width, null);
30946         }
30947         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30948     }
30949 });
30950
30951 Roo.SouthLayoutRegion = function(mgr, config){
30952     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30953     if(this.split){
30954         this.split.placement = Roo.SplitBar.BOTTOM;
30955         this.split.orientation = Roo.SplitBar.VERTICAL;
30956         this.split.el.addClass("x-layout-split-v");
30957     }
30958     var size = config.initialSize || config.height;
30959     if(typeof size != "undefined"){
30960         this.el.setHeight(size);
30961     }
30962 };
30963 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30964     orientation: Roo.SplitBar.VERTICAL,
30965     getBox : function(){
30966         if(this.collapsed){
30967             return this.collapsedEl.getBox();
30968         }
30969         var box = this.el.getBox();
30970         if(this.split){
30971             var sh = this.split.el.getHeight();
30972             box.height += sh;
30973             box.y -= sh;
30974         }
30975         return box;
30976     },
30977     
30978     updateBox : function(box){
30979         if(this.split && !this.collapsed){
30980             var sh = this.split.el.getHeight();
30981             box.height -= sh;
30982             box.y += sh;
30983             this.split.el.setLeft(box.x);
30984             this.split.el.setTop(box.y-sh);
30985             this.split.el.setWidth(box.width);
30986         }
30987         if(this.collapsed){
30988             this.updateBody(box.width, null);
30989         }
30990         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30991     }
30992 });
30993
30994 Roo.EastLayoutRegion = function(mgr, config){
30995     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
30996     if(this.split){
30997         this.split.placement = Roo.SplitBar.RIGHT;
30998         this.split.orientation = Roo.SplitBar.HORIZONTAL;
30999         this.split.el.addClass("x-layout-split-h");
31000     }
31001     var size = config.initialSize || config.width;
31002     if(typeof size != "undefined"){
31003         this.el.setWidth(size);
31004     }
31005 };
31006 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31007     orientation: Roo.SplitBar.HORIZONTAL,
31008     getBox : function(){
31009         if(this.collapsed){
31010             return this.collapsedEl.getBox();
31011         }
31012         var box = this.el.getBox();
31013         if(this.split){
31014             var sw = this.split.el.getWidth();
31015             box.width += sw;
31016             box.x -= sw;
31017         }
31018         return box;
31019     },
31020
31021     updateBox : function(box){
31022         if(this.split && !this.collapsed){
31023             var sw = this.split.el.getWidth();
31024             box.width -= sw;
31025             this.split.el.setLeft(box.x);
31026             this.split.el.setTop(box.y);
31027             this.split.el.setHeight(box.height);
31028             box.x += sw;
31029         }
31030         if(this.collapsed){
31031             this.updateBody(null, box.height);
31032         }
31033         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31034     }
31035 });
31036
31037 Roo.WestLayoutRegion = function(mgr, config){
31038     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31039     if(this.split){
31040         this.split.placement = Roo.SplitBar.LEFT;
31041         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31042         this.split.el.addClass("x-layout-split-h");
31043     }
31044     var size = config.initialSize || config.width;
31045     if(typeof size != "undefined"){
31046         this.el.setWidth(size);
31047     }
31048 };
31049 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31050     orientation: Roo.SplitBar.HORIZONTAL,
31051     getBox : function(){
31052         if(this.collapsed){
31053             return this.collapsedEl.getBox();
31054         }
31055         var box = this.el.getBox();
31056         if(this.split){
31057             box.width += this.split.el.getWidth();
31058         }
31059         return box;
31060     },
31061     
31062     updateBox : function(box){
31063         if(this.split && !this.collapsed){
31064             var sw = this.split.el.getWidth();
31065             box.width -= sw;
31066             this.split.el.setLeft(box.x+box.width);
31067             this.split.el.setTop(box.y);
31068             this.split.el.setHeight(box.height);
31069         }
31070         if(this.collapsed){
31071             this.updateBody(null, box.height);
31072         }
31073         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31074     }
31075 });
31076 /*
31077  * Based on:
31078  * Ext JS Library 1.1.1
31079  * Copyright(c) 2006-2007, Ext JS, LLC.
31080  *
31081  * Originally Released Under LGPL - original licence link has changed is not relivant.
31082  *
31083  * Fork - LGPL
31084  * <script type="text/javascript">
31085  */
31086  
31087  
31088 /*
31089  * Private internal class for reading and applying state
31090  */
31091 Roo.LayoutStateManager = function(layout){
31092      // default empty state
31093      this.state = {
31094         north: {},
31095         south: {},
31096         east: {},
31097         west: {}       
31098     };
31099 };
31100
31101 Roo.LayoutStateManager.prototype = {
31102     init : function(layout, provider){
31103         this.provider = provider;
31104         var state = provider.get(layout.id+"-layout-state");
31105         if(state){
31106             var wasUpdating = layout.isUpdating();
31107             if(!wasUpdating){
31108                 layout.beginUpdate();
31109             }
31110             for(var key in state){
31111                 if(typeof state[key] != "function"){
31112                     var rstate = state[key];
31113                     var r = layout.getRegion(key);
31114                     if(r && rstate){
31115                         if(rstate.size){
31116                             r.resizeTo(rstate.size);
31117                         }
31118                         if(rstate.collapsed == true){
31119                             r.collapse(true);
31120                         }else{
31121                             r.expand(null, true);
31122                         }
31123                     }
31124                 }
31125             }
31126             if(!wasUpdating){
31127                 layout.endUpdate();
31128             }
31129             this.state = state; 
31130         }
31131         this.layout = layout;
31132         layout.on("regionresized", this.onRegionResized, this);
31133         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31134         layout.on("regionexpanded", this.onRegionExpanded, this);
31135     },
31136     
31137     storeState : function(){
31138         this.provider.set(this.layout.id+"-layout-state", this.state);
31139     },
31140     
31141     onRegionResized : function(region, newSize){
31142         this.state[region.getPosition()].size = newSize;
31143         this.storeState();
31144     },
31145     
31146     onRegionCollapsed : function(region){
31147         this.state[region.getPosition()].collapsed = true;
31148         this.storeState();
31149     },
31150     
31151     onRegionExpanded : function(region){
31152         this.state[region.getPosition()].collapsed = false;
31153         this.storeState();
31154     }
31155 };/*
31156  * Based on:
31157  * Ext JS Library 1.1.1
31158  * Copyright(c) 2006-2007, Ext JS, LLC.
31159  *
31160  * Originally Released Under LGPL - original licence link has changed is not relivant.
31161  *
31162  * Fork - LGPL
31163  * <script type="text/javascript">
31164  */
31165 /**
31166  * @class Roo.ContentPanel
31167  * @extends Roo.util.Observable
31168  * A basic ContentPanel element.
31169  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31170  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31171  * @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
31172  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31173  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31174  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31175  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31176  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31177  * @cfg {String} title          The title for this panel
31178  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31179  * @cfg {String} url            Calls {@link #setUrl} with this value
31180  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31181  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31182  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31183  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31184
31185  * @constructor
31186  * Create a new ContentPanel.
31187  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31188  * @param {String/Object} config A string to set only the title or a config object
31189  * @param {String} content (optional) Set the HTML content for this panel
31190  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31191  */
31192 Roo.ContentPanel = function(el, config, content){
31193     
31194      
31195     /*
31196     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31197         config = el;
31198         el = Roo.id();
31199     }
31200     if (config && config.parentLayout) { 
31201         el = config.parentLayout.el.createChild(); 
31202     }
31203     */
31204     if(el.autoCreate){ // xtype is available if this is called from factory
31205         config = el;
31206         el = Roo.id();
31207     }
31208     this.el = Roo.get(el);
31209     if(!this.el && config && config.autoCreate){
31210         if(typeof config.autoCreate == "object"){
31211             if(!config.autoCreate.id){
31212                 config.autoCreate.id = config.id||el;
31213             }
31214             this.el = Roo.DomHelper.append(document.body,
31215                         config.autoCreate, true);
31216         }else{
31217             this.el = Roo.DomHelper.append(document.body,
31218                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31219         }
31220     }
31221     this.closable = false;
31222     this.loaded = false;
31223     this.active = false;
31224     if(typeof config == "string"){
31225         this.title = config;
31226     }else{
31227         Roo.apply(this, config);
31228     }
31229     
31230     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31231         this.wrapEl = this.el.wrap();
31232         this.toolbar.container = this.el.insertSibling(false, 'before');
31233         this.toolbar = new Roo.Toolbar(this.toolbar);
31234     }
31235     
31236     // xtype created footer. - not sure if will work as we normally have to render first..
31237     if (this.footer && !this.footer.el && this.footer.xtype) {
31238         if (!this.wrapEl) {
31239             this.wrapEl = this.el.wrap();
31240         }
31241     
31242         this.footer.container = this.wrapEl.createChild();
31243          
31244         this.footer = Roo.factory(this.footer, Roo);
31245         
31246     }
31247     
31248     if(this.resizeEl){
31249         this.resizeEl = Roo.get(this.resizeEl, true);
31250     }else{
31251         this.resizeEl = this.el;
31252     }
31253     // handle view.xtype
31254     
31255  
31256     
31257     
31258     this.addEvents({
31259         /**
31260          * @event activate
31261          * Fires when this panel is activated. 
31262          * @param {Roo.ContentPanel} this
31263          */
31264         "activate" : true,
31265         /**
31266          * @event deactivate
31267          * Fires when this panel is activated. 
31268          * @param {Roo.ContentPanel} this
31269          */
31270         "deactivate" : true,
31271
31272         /**
31273          * @event resize
31274          * Fires when this panel is resized if fitToFrame is true.
31275          * @param {Roo.ContentPanel} this
31276          * @param {Number} width The width after any component adjustments
31277          * @param {Number} height The height after any component adjustments
31278          */
31279         "resize" : true,
31280         
31281          /**
31282          * @event render
31283          * Fires when this tab is created
31284          * @param {Roo.ContentPanel} this
31285          */
31286         "render" : true
31287          
31288         
31289     });
31290     
31291
31292     
31293     
31294     if(this.autoScroll){
31295         this.resizeEl.setStyle("overflow", "auto");
31296     } else {
31297         // fix randome scrolling
31298         this.el.on('scroll', function() {
31299             Roo.log('fix random scolling');
31300             this.scrollTo('top',0); 
31301         });
31302     }
31303     content = content || this.content;
31304     if(content){
31305         this.setContent(content);
31306     }
31307     if(config && config.url){
31308         this.setUrl(this.url, this.params, this.loadOnce);
31309     }
31310     
31311     
31312     
31313     Roo.ContentPanel.superclass.constructor.call(this);
31314     
31315     if (this.view && typeof(this.view.xtype) != 'undefined') {
31316         this.view.el = this.el.appendChild(document.createElement("div"));
31317         this.view = Roo.factory(this.view); 
31318         this.view.render  &&  this.view.render(false, '');  
31319     }
31320     
31321     
31322     this.fireEvent('render', this);
31323 };
31324
31325 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31326     tabTip:'',
31327     setRegion : function(region){
31328         this.region = region;
31329         if(region){
31330            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31331         }else{
31332            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31333         } 
31334     },
31335     
31336     /**
31337      * Returns the toolbar for this Panel if one was configured. 
31338      * @return {Roo.Toolbar} 
31339      */
31340     getToolbar : function(){
31341         return this.toolbar;
31342     },
31343     
31344     setActiveState : function(active){
31345         this.active = active;
31346         if(!active){
31347             this.fireEvent("deactivate", this);
31348         }else{
31349             this.fireEvent("activate", this);
31350         }
31351     },
31352     /**
31353      * Updates this panel's element
31354      * @param {String} content The new content
31355      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31356     */
31357     setContent : function(content, loadScripts){
31358         this.el.update(content, loadScripts);
31359     },
31360
31361     ignoreResize : function(w, h){
31362         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31363             return true;
31364         }else{
31365             this.lastSize = {width: w, height: h};
31366             return false;
31367         }
31368     },
31369     /**
31370      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31371      * @return {Roo.UpdateManager} The UpdateManager
31372      */
31373     getUpdateManager : function(){
31374         return this.el.getUpdateManager();
31375     },
31376      /**
31377      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31378      * @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:
31379 <pre><code>
31380 panel.load({
31381     url: "your-url.php",
31382     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31383     callback: yourFunction,
31384     scope: yourObject, //(optional scope)
31385     discardUrl: false,
31386     nocache: false,
31387     text: "Loading...",
31388     timeout: 30,
31389     scripts: false
31390 });
31391 </code></pre>
31392      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31393      * 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.
31394      * @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}
31395      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31396      * @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.
31397      * @return {Roo.ContentPanel} this
31398      */
31399     load : function(){
31400         var um = this.el.getUpdateManager();
31401         um.update.apply(um, arguments);
31402         return this;
31403     },
31404
31405
31406     /**
31407      * 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.
31408      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31409      * @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)
31410      * @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)
31411      * @return {Roo.UpdateManager} The UpdateManager
31412      */
31413     setUrl : function(url, params, loadOnce){
31414         if(this.refreshDelegate){
31415             this.removeListener("activate", this.refreshDelegate);
31416         }
31417         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31418         this.on("activate", this.refreshDelegate);
31419         return this.el.getUpdateManager();
31420     },
31421     
31422     _handleRefresh : function(url, params, loadOnce){
31423         if(!loadOnce || !this.loaded){
31424             var updater = this.el.getUpdateManager();
31425             updater.update(url, params, this._setLoaded.createDelegate(this));
31426         }
31427     },
31428     
31429     _setLoaded : function(){
31430         this.loaded = true;
31431     }, 
31432     
31433     /**
31434      * Returns this panel's id
31435      * @return {String} 
31436      */
31437     getId : function(){
31438         return this.el.id;
31439     },
31440     
31441     /** 
31442      * Returns this panel's element - used by regiosn to add.
31443      * @return {Roo.Element} 
31444      */
31445     getEl : function(){
31446         return this.wrapEl || this.el;
31447     },
31448     
31449     adjustForComponents : function(width, height)
31450     {
31451         //Roo.log('adjustForComponents ');
31452         if(this.resizeEl != this.el){
31453             width -= this.el.getFrameWidth('lr');
31454             height -= this.el.getFrameWidth('tb');
31455         }
31456         if(this.toolbar){
31457             var te = this.toolbar.getEl();
31458             height -= te.getHeight();
31459             te.setWidth(width);
31460         }
31461         if(this.footer){
31462             var te = this.footer.getEl();
31463             //Roo.log("footer:" + te.getHeight());
31464             
31465             height -= te.getHeight();
31466             te.setWidth(width);
31467         }
31468         
31469         
31470         if(this.adjustments){
31471             width += this.adjustments[0];
31472             height += this.adjustments[1];
31473         }
31474         return {"width": width, "height": height};
31475     },
31476     
31477     setSize : function(width, height){
31478         if(this.fitToFrame && !this.ignoreResize(width, height)){
31479             if(this.fitContainer && this.resizeEl != this.el){
31480                 this.el.setSize(width, height);
31481             }
31482             var size = this.adjustForComponents(width, height);
31483             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31484             this.fireEvent('resize', this, size.width, size.height);
31485         }
31486     },
31487     
31488     /**
31489      * Returns this panel's title
31490      * @return {String} 
31491      */
31492     getTitle : function(){
31493         return this.title;
31494     },
31495     
31496     /**
31497      * Set this panel's title
31498      * @param {String} title
31499      */
31500     setTitle : function(title){
31501         this.title = title;
31502         if(this.region){
31503             this.region.updatePanelTitle(this, title);
31504         }
31505     },
31506     
31507     /**
31508      * Returns true is this panel was configured to be closable
31509      * @return {Boolean} 
31510      */
31511     isClosable : function(){
31512         return this.closable;
31513     },
31514     
31515     beforeSlide : function(){
31516         this.el.clip();
31517         this.resizeEl.clip();
31518     },
31519     
31520     afterSlide : function(){
31521         this.el.unclip();
31522         this.resizeEl.unclip();
31523     },
31524     
31525     /**
31526      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31527      *   Will fail silently if the {@link #setUrl} method has not been called.
31528      *   This does not activate the panel, just updates its content.
31529      */
31530     refresh : function(){
31531         if(this.refreshDelegate){
31532            this.loaded = false;
31533            this.refreshDelegate();
31534         }
31535     },
31536     
31537     /**
31538      * Destroys this panel
31539      */
31540     destroy : function(){
31541         this.el.removeAllListeners();
31542         var tempEl = document.createElement("span");
31543         tempEl.appendChild(this.el.dom);
31544         tempEl.innerHTML = "";
31545         this.el.remove();
31546         this.el = null;
31547     },
31548     
31549     /**
31550      * form - if the content panel contains a form - this is a reference to it.
31551      * @type {Roo.form.Form}
31552      */
31553     form : false,
31554     /**
31555      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31556      *    This contains a reference to it.
31557      * @type {Roo.View}
31558      */
31559     view : false,
31560     
31561       /**
31562      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31563      * <pre><code>
31564
31565 layout.addxtype({
31566        xtype : 'Form',
31567        items: [ .... ]
31568    }
31569 );
31570
31571 </code></pre>
31572      * @param {Object} cfg Xtype definition of item to add.
31573      */
31574     
31575     addxtype : function(cfg) {
31576         // add form..
31577         if (cfg.xtype.match(/^Form$/)) {
31578             
31579             var el;
31580             //if (this.footer) {
31581             //    el = this.footer.container.insertSibling(false, 'before');
31582             //} else {
31583                 el = this.el.createChild();
31584             //}
31585
31586             this.form = new  Roo.form.Form(cfg);
31587             
31588             
31589             if ( this.form.allItems.length) {
31590                 this.form.render(el.dom);
31591             }
31592             return this.form;
31593         }
31594         // should only have one of theses..
31595         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31596             // views.. should not be just added - used named prop 'view''
31597             
31598             cfg.el = this.el.appendChild(document.createElement("div"));
31599             // factory?
31600             
31601             var ret = new Roo.factory(cfg);
31602              
31603              ret.render && ret.render(false, ''); // render blank..
31604             this.view = ret;
31605             return ret;
31606         }
31607         return false;
31608     }
31609 });
31610
31611 /**
31612  * @class Roo.GridPanel
31613  * @extends Roo.ContentPanel
31614  * @constructor
31615  * Create a new GridPanel.
31616  * @param {Roo.grid.Grid} grid The grid for this panel
31617  * @param {String/Object} config A string to set only the panel's title, or a config object
31618  */
31619 Roo.GridPanel = function(grid, config){
31620     
31621   
31622     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31623         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31624         
31625     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31626     
31627     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31628     
31629     if(this.toolbar){
31630         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31631     }
31632     // xtype created footer. - not sure if will work as we normally have to render first..
31633     if (this.footer && !this.footer.el && this.footer.xtype) {
31634         
31635         this.footer.container = this.grid.getView().getFooterPanel(true);
31636         this.footer.dataSource = this.grid.dataSource;
31637         this.footer = Roo.factory(this.footer, Roo);
31638         
31639     }
31640     
31641     grid.monitorWindowResize = false; // turn off autosizing
31642     grid.autoHeight = false;
31643     grid.autoWidth = false;
31644     this.grid = grid;
31645     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31646 };
31647
31648 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31649     getId : function(){
31650         return this.grid.id;
31651     },
31652     
31653     /**
31654      * Returns the grid for this panel
31655      * @return {Roo.grid.Grid} 
31656      */
31657     getGrid : function(){
31658         return this.grid;    
31659     },
31660     
31661     setSize : function(width, height){
31662         if(!this.ignoreResize(width, height)){
31663             var grid = this.grid;
31664             var size = this.adjustForComponents(width, height);
31665             grid.getGridEl().setSize(size.width, size.height);
31666             grid.autoSize();
31667         }
31668     },
31669     
31670     beforeSlide : function(){
31671         this.grid.getView().scroller.clip();
31672     },
31673     
31674     afterSlide : function(){
31675         this.grid.getView().scroller.unclip();
31676     },
31677     
31678     destroy : function(){
31679         this.grid.destroy();
31680         delete this.grid;
31681         Roo.GridPanel.superclass.destroy.call(this); 
31682     }
31683 });
31684
31685
31686 /**
31687  * @class Roo.NestedLayoutPanel
31688  * @extends Roo.ContentPanel
31689  * @constructor
31690  * Create a new NestedLayoutPanel.
31691  * 
31692  * 
31693  * @param {Roo.BorderLayout} layout The layout for this panel
31694  * @param {String/Object} config A string to set only the title or a config object
31695  */
31696 Roo.NestedLayoutPanel = function(layout, config)
31697 {
31698     // construct with only one argument..
31699     /* FIXME - implement nicer consturctors
31700     if (layout.layout) {
31701         config = layout;
31702         layout = config.layout;
31703         delete config.layout;
31704     }
31705     if (layout.xtype && !layout.getEl) {
31706         // then layout needs constructing..
31707         layout = Roo.factory(layout, Roo);
31708     }
31709     */
31710     
31711     
31712     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31713     
31714     layout.monitorWindowResize = false; // turn off autosizing
31715     this.layout = layout;
31716     this.layout.getEl().addClass("x-layout-nested-layout");
31717     
31718     
31719     
31720     
31721 };
31722
31723 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31724
31725     setSize : function(width, height){
31726         if(!this.ignoreResize(width, height)){
31727             var size = this.adjustForComponents(width, height);
31728             var el = this.layout.getEl();
31729             el.setSize(size.width, size.height);
31730             var touch = el.dom.offsetWidth;
31731             this.layout.layout();
31732             // ie requires a double layout on the first pass
31733             if(Roo.isIE && !this.initialized){
31734                 this.initialized = true;
31735                 this.layout.layout();
31736             }
31737         }
31738     },
31739     
31740     // activate all subpanels if not currently active..
31741     
31742     setActiveState : function(active){
31743         this.active = active;
31744         if(!active){
31745             this.fireEvent("deactivate", this);
31746             return;
31747         }
31748         
31749         this.fireEvent("activate", this);
31750         // not sure if this should happen before or after..
31751         if (!this.layout) {
31752             return; // should not happen..
31753         }
31754         var reg = false;
31755         for (var r in this.layout.regions) {
31756             reg = this.layout.getRegion(r);
31757             if (reg.getActivePanel()) {
31758                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31759                 reg.setActivePanel(reg.getActivePanel());
31760                 continue;
31761             }
31762             if (!reg.panels.length) {
31763                 continue;
31764             }
31765             reg.showPanel(reg.getPanel(0));
31766         }
31767         
31768         
31769         
31770         
31771     },
31772     
31773     /**
31774      * Returns the nested BorderLayout for this panel
31775      * @return {Roo.BorderLayout} 
31776      */
31777     getLayout : function(){
31778         return this.layout;
31779     },
31780     
31781      /**
31782      * Adds a xtype elements to the layout of the nested panel
31783      * <pre><code>
31784
31785 panel.addxtype({
31786        xtype : 'ContentPanel',
31787        region: 'west',
31788        items: [ .... ]
31789    }
31790 );
31791
31792 panel.addxtype({
31793         xtype : 'NestedLayoutPanel',
31794         region: 'west',
31795         layout: {
31796            center: { },
31797            west: { }   
31798         },
31799         items : [ ... list of content panels or nested layout panels.. ]
31800    }
31801 );
31802 </code></pre>
31803      * @param {Object} cfg Xtype definition of item to add.
31804      */
31805     addxtype : function(cfg) {
31806         return this.layout.addxtype(cfg);
31807     
31808     }
31809 });
31810
31811 Roo.ScrollPanel = function(el, config, content){
31812     config = config || {};
31813     config.fitToFrame = true;
31814     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31815     
31816     this.el.dom.style.overflow = "hidden";
31817     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31818     this.el.removeClass("x-layout-inactive-content");
31819     this.el.on("mousewheel", this.onWheel, this);
31820
31821     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31822     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31823     up.unselectable(); down.unselectable();
31824     up.on("click", this.scrollUp, this);
31825     down.on("click", this.scrollDown, this);
31826     up.addClassOnOver("x-scroller-btn-over");
31827     down.addClassOnOver("x-scroller-btn-over");
31828     up.addClassOnClick("x-scroller-btn-click");
31829     down.addClassOnClick("x-scroller-btn-click");
31830     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31831
31832     this.resizeEl = this.el;
31833     this.el = wrap; this.up = up; this.down = down;
31834 };
31835
31836 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31837     increment : 100,
31838     wheelIncrement : 5,
31839     scrollUp : function(){
31840         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31841     },
31842
31843     scrollDown : function(){
31844         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31845     },
31846
31847     afterScroll : function(){
31848         var el = this.resizeEl;
31849         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31850         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31851         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31852     },
31853
31854     setSize : function(){
31855         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31856         this.afterScroll();
31857     },
31858
31859     onWheel : function(e){
31860         var d = e.getWheelDelta();
31861         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31862         this.afterScroll();
31863         e.stopEvent();
31864     },
31865
31866     setContent : function(content, loadScripts){
31867         this.resizeEl.update(content, loadScripts);
31868     }
31869
31870 });
31871
31872
31873
31874
31875
31876
31877
31878
31879
31880 /**
31881  * @class Roo.TreePanel
31882  * @extends Roo.ContentPanel
31883  * @constructor
31884  * Create a new TreePanel. - defaults to fit/scoll contents.
31885  * @param {String/Object} config A string to set only the panel's title, or a config object
31886  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31887  */
31888 Roo.TreePanel = function(config){
31889     var el = config.el;
31890     var tree = config.tree;
31891     delete config.tree; 
31892     delete config.el; // hopefull!
31893     
31894     // wrapper for IE7 strict & safari scroll issue
31895     
31896     var treeEl = el.createChild();
31897     config.resizeEl = treeEl;
31898     
31899     
31900     
31901     Roo.TreePanel.superclass.constructor.call(this, el, config);
31902  
31903  
31904     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31905     //console.log(tree);
31906     this.on('activate', function()
31907     {
31908         if (this.tree.rendered) {
31909             return;
31910         }
31911         //console.log('render tree');
31912         this.tree.render();
31913     });
31914     // this should not be needed.. - it's actually the 'el' that resizes?
31915     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31916     
31917     //this.on('resize',  function (cp, w, h) {
31918     //        this.tree.innerCt.setWidth(w);
31919     //        this.tree.innerCt.setHeight(h);
31920     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31921     //});
31922
31923         
31924     
31925 };
31926
31927 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31928     fitToFrame : true,
31929     autoScroll : true
31930 });
31931
31932
31933
31934
31935
31936
31937
31938
31939
31940
31941
31942 /*
31943  * Based on:
31944  * Ext JS Library 1.1.1
31945  * Copyright(c) 2006-2007, Ext JS, LLC.
31946  *
31947  * Originally Released Under LGPL - original licence link has changed is not relivant.
31948  *
31949  * Fork - LGPL
31950  * <script type="text/javascript">
31951  */
31952  
31953
31954 /**
31955  * @class Roo.ReaderLayout
31956  * @extends Roo.BorderLayout
31957  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31958  * center region containing two nested regions (a top one for a list view and one for item preview below),
31959  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31960  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31961  * expedites the setup of the overall layout and regions for this common application style.
31962  * Example:
31963  <pre><code>
31964 var reader = new Roo.ReaderLayout();
31965 var CP = Roo.ContentPanel;  // shortcut for adding
31966
31967 reader.beginUpdate();
31968 reader.add("north", new CP("north", "North"));
31969 reader.add("west", new CP("west", {title: "West"}));
31970 reader.add("east", new CP("east", {title: "East"}));
31971
31972 reader.regions.listView.add(new CP("listView", "List"));
31973 reader.regions.preview.add(new CP("preview", "Preview"));
31974 reader.endUpdate();
31975 </code></pre>
31976 * @constructor
31977 * Create a new ReaderLayout
31978 * @param {Object} config Configuration options
31979 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31980 * document.body if omitted)
31981 */
31982 Roo.ReaderLayout = function(config, renderTo){
31983     var c = config || {size:{}};
31984     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31985         north: c.north !== false ? Roo.apply({
31986             split:false,
31987             initialSize: 32,
31988             titlebar: false
31989         }, c.north) : false,
31990         west: c.west !== false ? Roo.apply({
31991             split:true,
31992             initialSize: 200,
31993             minSize: 175,
31994             maxSize: 400,
31995             titlebar: true,
31996             collapsible: true,
31997             animate: true,
31998             margins:{left:5,right:0,bottom:5,top:5},
31999             cmargins:{left:5,right:5,bottom:5,top:5}
32000         }, c.west) : false,
32001         east: c.east !== false ? Roo.apply({
32002             split:true,
32003             initialSize: 200,
32004             minSize: 175,
32005             maxSize: 400,
32006             titlebar: true,
32007             collapsible: true,
32008             animate: true,
32009             margins:{left:0,right:5,bottom:5,top:5},
32010             cmargins:{left:5,right:5,bottom:5,top:5}
32011         }, c.east) : false,
32012         center: Roo.apply({
32013             tabPosition: 'top',
32014             autoScroll:false,
32015             closeOnTab: true,
32016             titlebar:false,
32017             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32018         }, c.center)
32019     });
32020
32021     this.el.addClass('x-reader');
32022
32023     this.beginUpdate();
32024
32025     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32026         south: c.preview !== false ? Roo.apply({
32027             split:true,
32028             initialSize: 200,
32029             minSize: 100,
32030             autoScroll:true,
32031             collapsible:true,
32032             titlebar: true,
32033             cmargins:{top:5,left:0, right:0, bottom:0}
32034         }, c.preview) : false,
32035         center: Roo.apply({
32036             autoScroll:false,
32037             titlebar:false,
32038             minHeight:200
32039         }, c.listView)
32040     });
32041     this.add('center', new Roo.NestedLayoutPanel(inner,
32042             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32043
32044     this.endUpdate();
32045
32046     this.regions.preview = inner.getRegion('south');
32047     this.regions.listView = inner.getRegion('center');
32048 };
32049
32050 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32051  * Based on:
32052  * Ext JS Library 1.1.1
32053  * Copyright(c) 2006-2007, Ext JS, LLC.
32054  *
32055  * Originally Released Under LGPL - original licence link has changed is not relivant.
32056  *
32057  * Fork - LGPL
32058  * <script type="text/javascript">
32059  */
32060  
32061 /**
32062  * @class Roo.grid.Grid
32063  * @extends Roo.util.Observable
32064  * This class represents the primary interface of a component based grid control.
32065  * <br><br>Usage:<pre><code>
32066  var grid = new Roo.grid.Grid("my-container-id", {
32067      ds: myDataStore,
32068      cm: myColModel,
32069      selModel: mySelectionModel,
32070      autoSizeColumns: true,
32071      monitorWindowResize: false,
32072      trackMouseOver: true
32073  });
32074  // set any options
32075  grid.render();
32076  * </code></pre>
32077  * <b>Common Problems:</b><br/>
32078  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32079  * element will correct this<br/>
32080  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32081  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32082  * are unpredictable.<br/>
32083  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32084  * grid to calculate dimensions/offsets.<br/>
32085   * @constructor
32086  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32087  * The container MUST have some type of size defined for the grid to fill. The container will be
32088  * automatically set to position relative if it isn't already.
32089  * @param {Object} config A config object that sets properties on this grid.
32090  */
32091 Roo.grid.Grid = function(container, config){
32092         // initialize the container
32093         this.container = Roo.get(container);
32094         this.container.update("");
32095         this.container.setStyle("overflow", "hidden");
32096     this.container.addClass('x-grid-container');
32097
32098     this.id = this.container.id;
32099
32100     Roo.apply(this, config);
32101     // check and correct shorthanded configs
32102     if(this.ds){
32103         this.dataSource = this.ds;
32104         delete this.ds;
32105     }
32106     if(this.cm){
32107         this.colModel = this.cm;
32108         delete this.cm;
32109     }
32110     if(this.sm){
32111         this.selModel = this.sm;
32112         delete this.sm;
32113     }
32114
32115     if (this.selModel) {
32116         this.selModel = Roo.factory(this.selModel, Roo.grid);
32117         this.sm = this.selModel;
32118         this.sm.xmodule = this.xmodule || false;
32119     }
32120     if (typeof(this.colModel.config) == 'undefined') {
32121         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32122         this.cm = this.colModel;
32123         this.cm.xmodule = this.xmodule || false;
32124     }
32125     if (this.dataSource) {
32126         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32127         this.ds = this.dataSource;
32128         this.ds.xmodule = this.xmodule || false;
32129          
32130     }
32131     
32132     
32133     
32134     if(this.width){
32135         this.container.setWidth(this.width);
32136     }
32137
32138     if(this.height){
32139         this.container.setHeight(this.height);
32140     }
32141     /** @private */
32142         this.addEvents({
32143         // raw events
32144         /**
32145          * @event click
32146          * The raw click event for the entire grid.
32147          * @param {Roo.EventObject} e
32148          */
32149         "click" : true,
32150         /**
32151          * @event dblclick
32152          * The raw dblclick event for the entire grid.
32153          * @param {Roo.EventObject} e
32154          */
32155         "dblclick" : true,
32156         /**
32157          * @event contextmenu
32158          * The raw contextmenu event for the entire grid.
32159          * @param {Roo.EventObject} e
32160          */
32161         "contextmenu" : true,
32162         /**
32163          * @event mousedown
32164          * The raw mousedown event for the entire grid.
32165          * @param {Roo.EventObject} e
32166          */
32167         "mousedown" : true,
32168         /**
32169          * @event mouseup
32170          * The raw mouseup event for the entire grid.
32171          * @param {Roo.EventObject} e
32172          */
32173         "mouseup" : true,
32174         /**
32175          * @event mouseover
32176          * The raw mouseover event for the entire grid.
32177          * @param {Roo.EventObject} e
32178          */
32179         "mouseover" : true,
32180         /**
32181          * @event mouseout
32182          * The raw mouseout event for the entire grid.
32183          * @param {Roo.EventObject} e
32184          */
32185         "mouseout" : true,
32186         /**
32187          * @event keypress
32188          * The raw keypress event for the entire grid.
32189          * @param {Roo.EventObject} e
32190          */
32191         "keypress" : true,
32192         /**
32193          * @event keydown
32194          * The raw keydown event for the entire grid.
32195          * @param {Roo.EventObject} e
32196          */
32197         "keydown" : true,
32198
32199         // custom events
32200
32201         /**
32202          * @event cellclick
32203          * Fires when a cell is clicked
32204          * @param {Grid} this
32205          * @param {Number} rowIndex
32206          * @param {Number} columnIndex
32207          * @param {Roo.EventObject} e
32208          */
32209         "cellclick" : true,
32210         /**
32211          * @event celldblclick
32212          * Fires when a cell is double clicked
32213          * @param {Grid} this
32214          * @param {Number} rowIndex
32215          * @param {Number} columnIndex
32216          * @param {Roo.EventObject} e
32217          */
32218         "celldblclick" : true,
32219         /**
32220          * @event rowclick
32221          * Fires when a row is clicked
32222          * @param {Grid} this
32223          * @param {Number} rowIndex
32224          * @param {Roo.EventObject} e
32225          */
32226         "rowclick" : true,
32227         /**
32228          * @event rowdblclick
32229          * Fires when a row is double clicked
32230          * @param {Grid} this
32231          * @param {Number} rowIndex
32232          * @param {Roo.EventObject} e
32233          */
32234         "rowdblclick" : true,
32235         /**
32236          * @event headerclick
32237          * Fires when a header is clicked
32238          * @param {Grid} this
32239          * @param {Number} columnIndex
32240          * @param {Roo.EventObject} e
32241          */
32242         "headerclick" : true,
32243         /**
32244          * @event headerdblclick
32245          * Fires when a header cell is double clicked
32246          * @param {Grid} this
32247          * @param {Number} columnIndex
32248          * @param {Roo.EventObject} e
32249          */
32250         "headerdblclick" : true,
32251         /**
32252          * @event rowcontextmenu
32253          * Fires when a row is right clicked
32254          * @param {Grid} this
32255          * @param {Number} rowIndex
32256          * @param {Roo.EventObject} e
32257          */
32258         "rowcontextmenu" : true,
32259         /**
32260          * @event cellcontextmenu
32261          * Fires when a cell is right clicked
32262          * @param {Grid} this
32263          * @param {Number} rowIndex
32264          * @param {Number} cellIndex
32265          * @param {Roo.EventObject} e
32266          */
32267          "cellcontextmenu" : true,
32268         /**
32269          * @event headercontextmenu
32270          * Fires when a header is right clicked
32271          * @param {Grid} this
32272          * @param {Number} columnIndex
32273          * @param {Roo.EventObject} e
32274          */
32275         "headercontextmenu" : true,
32276         /**
32277          * @event bodyscroll
32278          * Fires when the body element is scrolled
32279          * @param {Number} scrollLeft
32280          * @param {Number} scrollTop
32281          */
32282         "bodyscroll" : true,
32283         /**
32284          * @event columnresize
32285          * Fires when the user resizes a column
32286          * @param {Number} columnIndex
32287          * @param {Number} newSize
32288          */
32289         "columnresize" : true,
32290         /**
32291          * @event columnmove
32292          * Fires when the user moves a column
32293          * @param {Number} oldIndex
32294          * @param {Number} newIndex
32295          */
32296         "columnmove" : true,
32297         /**
32298          * @event startdrag
32299          * Fires when row(s) start being dragged
32300          * @param {Grid} this
32301          * @param {Roo.GridDD} dd The drag drop object
32302          * @param {event} e The raw browser event
32303          */
32304         "startdrag" : true,
32305         /**
32306          * @event enddrag
32307          * Fires when a drag operation is complete
32308          * @param {Grid} this
32309          * @param {Roo.GridDD} dd The drag drop object
32310          * @param {event} e The raw browser event
32311          */
32312         "enddrag" : true,
32313         /**
32314          * @event dragdrop
32315          * Fires when dragged row(s) are dropped on a valid DD target
32316          * @param {Grid} this
32317          * @param {Roo.GridDD} dd The drag drop object
32318          * @param {String} targetId The target drag drop object
32319          * @param {event} e The raw browser event
32320          */
32321         "dragdrop" : true,
32322         /**
32323          * @event dragover
32324          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32325          * @param {Grid} this
32326          * @param {Roo.GridDD} dd The drag drop object
32327          * @param {String} targetId The target drag drop object
32328          * @param {event} e The raw browser event
32329          */
32330         "dragover" : true,
32331         /**
32332          * @event dragenter
32333          *  Fires when the dragged row(s) first cross another DD target while being dragged
32334          * @param {Grid} this
32335          * @param {Roo.GridDD} dd The drag drop object
32336          * @param {String} targetId The target drag drop object
32337          * @param {event} e The raw browser event
32338          */
32339         "dragenter" : true,
32340         /**
32341          * @event dragout
32342          * Fires when the dragged row(s) leave another DD target while being dragged
32343          * @param {Grid} this
32344          * @param {Roo.GridDD} dd The drag drop object
32345          * @param {String} targetId The target drag drop object
32346          * @param {event} e The raw browser event
32347          */
32348         "dragout" : true,
32349         /**
32350          * @event rowclass
32351          * Fires when a row is rendered, so you can change add a style to it.
32352          * @param {GridView} gridview   The grid view
32353          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32354          */
32355         'rowclass' : true,
32356
32357         /**
32358          * @event render
32359          * Fires when the grid is rendered
32360          * @param {Grid} grid
32361          */
32362         'render' : true
32363     });
32364
32365     Roo.grid.Grid.superclass.constructor.call(this);
32366 };
32367 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32368     
32369     /**
32370      * @cfg {String} ddGroup - drag drop group.
32371      */
32372
32373     /**
32374      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32375      */
32376     minColumnWidth : 25,
32377
32378     /**
32379      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32380      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32381      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32382      */
32383     autoSizeColumns : false,
32384
32385     /**
32386      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32387      */
32388     autoSizeHeaders : true,
32389
32390     /**
32391      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32392      */
32393     monitorWindowResize : true,
32394
32395     /**
32396      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32397      * rows measured to get a columns size. Default is 0 (all rows).
32398      */
32399     maxRowsToMeasure : 0,
32400
32401     /**
32402      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32403      */
32404     trackMouseOver : true,
32405
32406     /**
32407     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32408     */
32409     
32410     /**
32411     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32412     */
32413     enableDragDrop : false,
32414     
32415     /**
32416     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32417     */
32418     enableColumnMove : true,
32419     
32420     /**
32421     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32422     */
32423     enableColumnHide : true,
32424     
32425     /**
32426     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32427     */
32428     enableRowHeightSync : false,
32429     
32430     /**
32431     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32432     */
32433     stripeRows : true,
32434     
32435     /**
32436     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32437     */
32438     autoHeight : false,
32439
32440     /**
32441      * @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.
32442      */
32443     autoExpandColumn : false,
32444
32445     /**
32446     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32447     * Default is 50.
32448     */
32449     autoExpandMin : 50,
32450
32451     /**
32452     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32453     */
32454     autoExpandMax : 1000,
32455
32456     /**
32457     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32458     */
32459     view : null,
32460
32461     /**
32462     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32463     */
32464     loadMask : false,
32465     /**
32466     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32467     */
32468     dropTarget: false,
32469     
32470    
32471     
32472     // private
32473     rendered : false,
32474
32475     /**
32476     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32477     * of a fixed width. Default is false.
32478     */
32479     /**
32480     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32481     */
32482     /**
32483      * Called once after all setup has been completed and the grid is ready to be rendered.
32484      * @return {Roo.grid.Grid} this
32485      */
32486     render : function()
32487     {
32488         var c = this.container;
32489         // try to detect autoHeight/width mode
32490         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32491             this.autoHeight = true;
32492         }
32493         var view = this.getView();
32494         view.init(this);
32495
32496         c.on("click", this.onClick, this);
32497         c.on("dblclick", this.onDblClick, this);
32498         c.on("contextmenu", this.onContextMenu, this);
32499         c.on("keydown", this.onKeyDown, this);
32500         if (Roo.isTouch) {
32501             c.on("touchstart", this.onTouchStart, this);
32502         }
32503
32504         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32505
32506         this.getSelectionModel().init(this);
32507
32508         view.render();
32509
32510         if(this.loadMask){
32511             this.loadMask = new Roo.LoadMask(this.container,
32512                     Roo.apply({store:this.dataSource}, this.loadMask));
32513         }
32514         
32515         
32516         if (this.toolbar && this.toolbar.xtype) {
32517             this.toolbar.container = this.getView().getHeaderPanel(true);
32518             this.toolbar = new Roo.Toolbar(this.toolbar);
32519         }
32520         if (this.footer && this.footer.xtype) {
32521             this.footer.dataSource = this.getDataSource();
32522             this.footer.container = this.getView().getFooterPanel(true);
32523             this.footer = Roo.factory(this.footer, Roo);
32524         }
32525         if (this.dropTarget && this.dropTarget.xtype) {
32526             delete this.dropTarget.xtype;
32527             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32528         }
32529         
32530         
32531         this.rendered = true;
32532         this.fireEvent('render', this);
32533         return this;
32534     },
32535
32536     /**
32537      * Reconfigures the grid to use a different Store and Column Model.
32538      * The View will be bound to the new objects and refreshed.
32539      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32540      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32541      */
32542     reconfigure : function(dataSource, colModel){
32543         if(this.loadMask){
32544             this.loadMask.destroy();
32545             this.loadMask = new Roo.LoadMask(this.container,
32546                     Roo.apply({store:dataSource}, this.loadMask));
32547         }
32548         this.view.bind(dataSource, colModel);
32549         this.dataSource = dataSource;
32550         this.colModel = colModel;
32551         this.view.refresh(true);
32552     },
32553     /**
32554      * addColumns
32555      * Add's a column, default at the end..
32556      
32557      * @param {int} position to add (default end)
32558      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32559      */
32560     addColumns : function(pos, ar)
32561     {
32562         
32563         for (var i =0;i< ar.length;i++) {
32564             var cfg = ar[i];
32565             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32566             this.cm.lookup[cfg.id] = cfg;
32567         }
32568         
32569         
32570         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32571             pos = this.cm.config.length; //this.cm.config.push(cfg);
32572         } 
32573         pos = Math.max(0,pos);
32574         ar.unshift(0);
32575         ar.unshift(pos);
32576         this.cm.config.splice.apply(this.cm.config, ar);
32577         
32578         
32579         
32580         this.view.generateRules(this.cm);
32581         this.view.refresh(true);
32582         
32583     },
32584     
32585     
32586     
32587     
32588     // private
32589     onKeyDown : function(e){
32590         this.fireEvent("keydown", e);
32591     },
32592
32593     /**
32594      * Destroy this grid.
32595      * @param {Boolean} removeEl True to remove the element
32596      */
32597     destroy : function(removeEl, keepListeners){
32598         if(this.loadMask){
32599             this.loadMask.destroy();
32600         }
32601         var c = this.container;
32602         c.removeAllListeners();
32603         this.view.destroy();
32604         this.colModel.purgeListeners();
32605         if(!keepListeners){
32606             this.purgeListeners();
32607         }
32608         c.update("");
32609         if(removeEl === true){
32610             c.remove();
32611         }
32612     },
32613
32614     // private
32615     processEvent : function(name, e){
32616         // does this fire select???
32617         //Roo.log('grid:processEvent '  + name);
32618         
32619         if (name != 'touchstart' ) {
32620             this.fireEvent(name, e);    
32621         }
32622         
32623         var t = e.getTarget();
32624         var v = this.view;
32625         var header = v.findHeaderIndex(t);
32626         if(header !== false){
32627             var ename = name == 'touchstart' ? 'click' : name;
32628              
32629             this.fireEvent("header" + ename, this, header, e);
32630         }else{
32631             var row = v.findRowIndex(t);
32632             var cell = v.findCellIndex(t);
32633             if (name == 'touchstart') {
32634                 // first touch is always a click.
32635                 // hopefull this happens after selection is updated.?
32636                 name = false;
32637                 
32638                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32639                     var cs = this.selModel.getSelectedCell();
32640                     if (row == cs[0] && cell == cs[1]){
32641                         name = 'dblclick';
32642                     }
32643                 }
32644                 if (typeof(this.selModel.getSelections) != 'undefined') {
32645                     var cs = this.selModel.getSelections();
32646                     var ds = this.dataSource;
32647                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32648                         name = 'dblclick';
32649                     }
32650                 }
32651                 if (!name) {
32652                     return;
32653                 }
32654             }
32655             
32656             
32657             if(row !== false){
32658                 this.fireEvent("row" + name, this, row, e);
32659                 if(cell !== false){
32660                     this.fireEvent("cell" + name, this, row, cell, e);
32661                 }
32662             }
32663         }
32664     },
32665
32666     // private
32667     onClick : function(e){
32668         this.processEvent("click", e);
32669     },
32670    // private
32671     onTouchStart : function(e){
32672         this.processEvent("touchstart", e);
32673     },
32674
32675     // private
32676     onContextMenu : function(e, t){
32677         this.processEvent("contextmenu", e);
32678     },
32679
32680     // private
32681     onDblClick : function(e){
32682         this.processEvent("dblclick", e);
32683     },
32684
32685     // private
32686     walkCells : function(row, col, step, fn, scope){
32687         var cm = this.colModel, clen = cm.getColumnCount();
32688         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32689         if(step < 0){
32690             if(col < 0){
32691                 row--;
32692                 first = false;
32693             }
32694             while(row >= 0){
32695                 if(!first){
32696                     col = clen-1;
32697                 }
32698                 first = false;
32699                 while(col >= 0){
32700                     if(fn.call(scope || this, row, col, cm) === true){
32701                         return [row, col];
32702                     }
32703                     col--;
32704                 }
32705                 row--;
32706             }
32707         } else {
32708             if(col >= clen){
32709                 row++;
32710                 first = false;
32711             }
32712             while(row < rlen){
32713                 if(!first){
32714                     col = 0;
32715                 }
32716                 first = false;
32717                 while(col < clen){
32718                     if(fn.call(scope || this, row, col, cm) === true){
32719                         return [row, col];
32720                     }
32721                     col++;
32722                 }
32723                 row++;
32724             }
32725         }
32726         return null;
32727     },
32728
32729     // private
32730     getSelections : function(){
32731         return this.selModel.getSelections();
32732     },
32733
32734     /**
32735      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32736      * but if manual update is required this method will initiate it.
32737      */
32738     autoSize : function(){
32739         if(this.rendered){
32740             this.view.layout();
32741             if(this.view.adjustForScroll){
32742                 this.view.adjustForScroll();
32743             }
32744         }
32745     },
32746
32747     /**
32748      * Returns the grid's underlying element.
32749      * @return {Element} The element
32750      */
32751     getGridEl : function(){
32752         return this.container;
32753     },
32754
32755     // private for compatibility, overridden by editor grid
32756     stopEditing : function(){},
32757
32758     /**
32759      * Returns the grid's SelectionModel.
32760      * @return {SelectionModel}
32761      */
32762     getSelectionModel : function(){
32763         if(!this.selModel){
32764             this.selModel = new Roo.grid.RowSelectionModel();
32765         }
32766         return this.selModel;
32767     },
32768
32769     /**
32770      * Returns the grid's DataSource.
32771      * @return {DataSource}
32772      */
32773     getDataSource : function(){
32774         return this.dataSource;
32775     },
32776
32777     /**
32778      * Returns the grid's ColumnModel.
32779      * @return {ColumnModel}
32780      */
32781     getColumnModel : function(){
32782         return this.colModel;
32783     },
32784
32785     /**
32786      * Returns the grid's GridView object.
32787      * @return {GridView}
32788      */
32789     getView : function(){
32790         if(!this.view){
32791             this.view = new Roo.grid.GridView(this.viewConfig);
32792         }
32793         return this.view;
32794     },
32795     /**
32796      * Called to get grid's drag proxy text, by default returns this.ddText.
32797      * @return {String}
32798      */
32799     getDragDropText : function(){
32800         var count = this.selModel.getCount();
32801         return String.format(this.ddText, count, count == 1 ? '' : 's');
32802     }
32803 });
32804 /**
32805  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32806  * %0 is replaced with the number of selected rows.
32807  * @type String
32808  */
32809 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32810  * Based on:
32811  * Ext JS Library 1.1.1
32812  * Copyright(c) 2006-2007, Ext JS, LLC.
32813  *
32814  * Originally Released Under LGPL - original licence link has changed is not relivant.
32815  *
32816  * Fork - LGPL
32817  * <script type="text/javascript">
32818  */
32819  
32820 Roo.grid.AbstractGridView = function(){
32821         this.grid = null;
32822         
32823         this.events = {
32824             "beforerowremoved" : true,
32825             "beforerowsinserted" : true,
32826             "beforerefresh" : true,
32827             "rowremoved" : true,
32828             "rowsinserted" : true,
32829             "rowupdated" : true,
32830             "refresh" : true
32831         };
32832     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32833 };
32834
32835 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32836     rowClass : "x-grid-row",
32837     cellClass : "x-grid-cell",
32838     tdClass : "x-grid-td",
32839     hdClass : "x-grid-hd",
32840     splitClass : "x-grid-hd-split",
32841     
32842     init: function(grid){
32843         this.grid = grid;
32844                 var cid = this.grid.getGridEl().id;
32845         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32846         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32847         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32848         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32849         },
32850         
32851     getColumnRenderers : function(){
32852         var renderers = [];
32853         var cm = this.grid.colModel;
32854         var colCount = cm.getColumnCount();
32855         for(var i = 0; i < colCount; i++){
32856             renderers[i] = cm.getRenderer(i);
32857         }
32858         return renderers;
32859     },
32860     
32861     getColumnIds : function(){
32862         var ids = [];
32863         var cm = this.grid.colModel;
32864         var colCount = cm.getColumnCount();
32865         for(var i = 0; i < colCount; i++){
32866             ids[i] = cm.getColumnId(i);
32867         }
32868         return ids;
32869     },
32870     
32871     getDataIndexes : function(){
32872         if(!this.indexMap){
32873             this.indexMap = this.buildIndexMap();
32874         }
32875         return this.indexMap.colToData;
32876     },
32877     
32878     getColumnIndexByDataIndex : function(dataIndex){
32879         if(!this.indexMap){
32880             this.indexMap = this.buildIndexMap();
32881         }
32882         return this.indexMap.dataToCol[dataIndex];
32883     },
32884     
32885     /**
32886      * Set a css style for a column dynamically. 
32887      * @param {Number} colIndex The index of the column
32888      * @param {String} name The css property name
32889      * @param {String} value The css value
32890      */
32891     setCSSStyle : function(colIndex, name, value){
32892         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32893         Roo.util.CSS.updateRule(selector, name, value);
32894     },
32895     
32896     generateRules : function(cm){
32897         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32898         Roo.util.CSS.removeStyleSheet(rulesId);
32899         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32900             var cid = cm.getColumnId(i);
32901             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32902                          this.tdSelector, cid, " {\n}\n",
32903                          this.hdSelector, cid, " {\n}\n",
32904                          this.splitSelector, cid, " {\n}\n");
32905         }
32906         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32907     }
32908 });/*
32909  * Based on:
32910  * Ext JS Library 1.1.1
32911  * Copyright(c) 2006-2007, Ext JS, LLC.
32912  *
32913  * Originally Released Under LGPL - original licence link has changed is not relivant.
32914  *
32915  * Fork - LGPL
32916  * <script type="text/javascript">
32917  */
32918
32919 // private
32920 // This is a support class used internally by the Grid components
32921 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32922     this.grid = grid;
32923     this.view = grid.getView();
32924     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32925     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32926     if(hd2){
32927         this.setHandleElId(Roo.id(hd));
32928         this.setOuterHandleElId(Roo.id(hd2));
32929     }
32930     this.scroll = false;
32931 };
32932 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32933     maxDragWidth: 120,
32934     getDragData : function(e){
32935         var t = Roo.lib.Event.getTarget(e);
32936         var h = this.view.findHeaderCell(t);
32937         if(h){
32938             return {ddel: h.firstChild, header:h};
32939         }
32940         return false;
32941     },
32942
32943     onInitDrag : function(e){
32944         this.view.headersDisabled = true;
32945         var clone = this.dragData.ddel.cloneNode(true);
32946         clone.id = Roo.id();
32947         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32948         this.proxy.update(clone);
32949         return true;
32950     },
32951
32952     afterValidDrop : function(){
32953         var v = this.view;
32954         setTimeout(function(){
32955             v.headersDisabled = false;
32956         }, 50);
32957     },
32958
32959     afterInvalidDrop : function(){
32960         var v = this.view;
32961         setTimeout(function(){
32962             v.headersDisabled = false;
32963         }, 50);
32964     }
32965 });
32966 /*
32967  * Based on:
32968  * Ext JS Library 1.1.1
32969  * Copyright(c) 2006-2007, Ext JS, LLC.
32970  *
32971  * Originally Released Under LGPL - original licence link has changed is not relivant.
32972  *
32973  * Fork - LGPL
32974  * <script type="text/javascript">
32975  */
32976 // private
32977 // This is a support class used internally by the Grid components
32978 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32979     this.grid = grid;
32980     this.view = grid.getView();
32981     // split the proxies so they don't interfere with mouse events
32982     this.proxyTop = Roo.DomHelper.append(document.body, {
32983         cls:"col-move-top", html:"&#160;"
32984     }, true);
32985     this.proxyBottom = Roo.DomHelper.append(document.body, {
32986         cls:"col-move-bottom", html:"&#160;"
32987     }, true);
32988     this.proxyTop.hide = this.proxyBottom.hide = function(){
32989         this.setLeftTop(-100,-100);
32990         this.setStyle("visibility", "hidden");
32991     };
32992     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32993     // temporarily disabled
32994     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
32995     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
32996 };
32997 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
32998     proxyOffsets : [-4, -9],
32999     fly: Roo.Element.fly,
33000
33001     getTargetFromEvent : function(e){
33002         var t = Roo.lib.Event.getTarget(e);
33003         var cindex = this.view.findCellIndex(t);
33004         if(cindex !== false){
33005             return this.view.getHeaderCell(cindex);
33006         }
33007         return null;
33008     },
33009
33010     nextVisible : function(h){
33011         var v = this.view, cm = this.grid.colModel;
33012         h = h.nextSibling;
33013         while(h){
33014             if(!cm.isHidden(v.getCellIndex(h))){
33015                 return h;
33016             }
33017             h = h.nextSibling;
33018         }
33019         return null;
33020     },
33021
33022     prevVisible : function(h){
33023         var v = this.view, cm = this.grid.colModel;
33024         h = h.prevSibling;
33025         while(h){
33026             if(!cm.isHidden(v.getCellIndex(h))){
33027                 return h;
33028             }
33029             h = h.prevSibling;
33030         }
33031         return null;
33032     },
33033
33034     positionIndicator : function(h, n, e){
33035         var x = Roo.lib.Event.getPageX(e);
33036         var r = Roo.lib.Dom.getRegion(n.firstChild);
33037         var px, pt, py = r.top + this.proxyOffsets[1];
33038         if((r.right - x) <= (r.right-r.left)/2){
33039             px = r.right+this.view.borderWidth;
33040             pt = "after";
33041         }else{
33042             px = r.left;
33043             pt = "before";
33044         }
33045         var oldIndex = this.view.getCellIndex(h);
33046         var newIndex = this.view.getCellIndex(n);
33047
33048         if(this.grid.colModel.isFixed(newIndex)){
33049             return false;
33050         }
33051
33052         var locked = this.grid.colModel.isLocked(newIndex);
33053
33054         if(pt == "after"){
33055             newIndex++;
33056         }
33057         if(oldIndex < newIndex){
33058             newIndex--;
33059         }
33060         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33061             return false;
33062         }
33063         px +=  this.proxyOffsets[0];
33064         this.proxyTop.setLeftTop(px, py);
33065         this.proxyTop.show();
33066         if(!this.bottomOffset){
33067             this.bottomOffset = this.view.mainHd.getHeight();
33068         }
33069         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33070         this.proxyBottom.show();
33071         return pt;
33072     },
33073
33074     onNodeEnter : function(n, dd, e, data){
33075         if(data.header != n){
33076             this.positionIndicator(data.header, n, e);
33077         }
33078     },
33079
33080     onNodeOver : function(n, dd, e, data){
33081         var result = false;
33082         if(data.header != n){
33083             result = this.positionIndicator(data.header, n, e);
33084         }
33085         if(!result){
33086             this.proxyTop.hide();
33087             this.proxyBottom.hide();
33088         }
33089         return result ? this.dropAllowed : this.dropNotAllowed;
33090     },
33091
33092     onNodeOut : function(n, dd, e, data){
33093         this.proxyTop.hide();
33094         this.proxyBottom.hide();
33095     },
33096
33097     onNodeDrop : function(n, dd, e, data){
33098         var h = data.header;
33099         if(h != n){
33100             var cm = this.grid.colModel;
33101             var x = Roo.lib.Event.getPageX(e);
33102             var r = Roo.lib.Dom.getRegion(n.firstChild);
33103             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33104             var oldIndex = this.view.getCellIndex(h);
33105             var newIndex = this.view.getCellIndex(n);
33106             var locked = cm.isLocked(newIndex);
33107             if(pt == "after"){
33108                 newIndex++;
33109             }
33110             if(oldIndex < newIndex){
33111                 newIndex--;
33112             }
33113             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33114                 return false;
33115             }
33116             cm.setLocked(oldIndex, locked, true);
33117             cm.moveColumn(oldIndex, newIndex);
33118             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33119             return true;
33120         }
33121         return false;
33122     }
33123 });
33124 /*
33125  * Based on:
33126  * Ext JS Library 1.1.1
33127  * Copyright(c) 2006-2007, Ext JS, LLC.
33128  *
33129  * Originally Released Under LGPL - original licence link has changed is not relivant.
33130  *
33131  * Fork - LGPL
33132  * <script type="text/javascript">
33133  */
33134   
33135 /**
33136  * @class Roo.grid.GridView
33137  * @extends Roo.util.Observable
33138  *
33139  * @constructor
33140  * @param {Object} config
33141  */
33142 Roo.grid.GridView = function(config){
33143     Roo.grid.GridView.superclass.constructor.call(this);
33144     this.el = null;
33145
33146     Roo.apply(this, config);
33147 };
33148
33149 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33150
33151     unselectable :  'unselectable="on"',
33152     unselectableCls :  'x-unselectable',
33153     
33154     
33155     rowClass : "x-grid-row",
33156
33157     cellClass : "x-grid-col",
33158
33159     tdClass : "x-grid-td",
33160
33161     hdClass : "x-grid-hd",
33162
33163     splitClass : "x-grid-split",
33164
33165     sortClasses : ["sort-asc", "sort-desc"],
33166
33167     enableMoveAnim : false,
33168
33169     hlColor: "C3DAF9",
33170
33171     dh : Roo.DomHelper,
33172
33173     fly : Roo.Element.fly,
33174
33175     css : Roo.util.CSS,
33176
33177     borderWidth: 1,
33178
33179     splitOffset: 3,
33180
33181     scrollIncrement : 22,
33182
33183     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33184
33185     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33186
33187     bind : function(ds, cm){
33188         if(this.ds){
33189             this.ds.un("load", this.onLoad, this);
33190             this.ds.un("datachanged", this.onDataChange, this);
33191             this.ds.un("add", this.onAdd, this);
33192             this.ds.un("remove", this.onRemove, this);
33193             this.ds.un("update", this.onUpdate, this);
33194             this.ds.un("clear", this.onClear, this);
33195         }
33196         if(ds){
33197             ds.on("load", this.onLoad, this);
33198             ds.on("datachanged", this.onDataChange, this);
33199             ds.on("add", this.onAdd, this);
33200             ds.on("remove", this.onRemove, this);
33201             ds.on("update", this.onUpdate, this);
33202             ds.on("clear", this.onClear, this);
33203         }
33204         this.ds = ds;
33205
33206         if(this.cm){
33207             this.cm.un("widthchange", this.onColWidthChange, this);
33208             this.cm.un("headerchange", this.onHeaderChange, this);
33209             this.cm.un("hiddenchange", this.onHiddenChange, this);
33210             this.cm.un("columnmoved", this.onColumnMove, this);
33211             this.cm.un("columnlockchange", this.onColumnLock, this);
33212         }
33213         if(cm){
33214             this.generateRules(cm);
33215             cm.on("widthchange", this.onColWidthChange, this);
33216             cm.on("headerchange", this.onHeaderChange, this);
33217             cm.on("hiddenchange", this.onHiddenChange, this);
33218             cm.on("columnmoved", this.onColumnMove, this);
33219             cm.on("columnlockchange", this.onColumnLock, this);
33220         }
33221         this.cm = cm;
33222     },
33223
33224     init: function(grid){
33225         Roo.grid.GridView.superclass.init.call(this, grid);
33226
33227         this.bind(grid.dataSource, grid.colModel);
33228
33229         grid.on("headerclick", this.handleHeaderClick, this);
33230
33231         if(grid.trackMouseOver){
33232             grid.on("mouseover", this.onRowOver, this);
33233             grid.on("mouseout", this.onRowOut, this);
33234         }
33235         grid.cancelTextSelection = function(){};
33236         this.gridId = grid.id;
33237
33238         var tpls = this.templates || {};
33239
33240         if(!tpls.master){
33241             tpls.master = new Roo.Template(
33242                '<div class="x-grid" hidefocus="true">',
33243                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33244                   '<div class="x-grid-topbar"></div>',
33245                   '<div class="x-grid-scroller"><div></div></div>',
33246                   '<div class="x-grid-locked">',
33247                       '<div class="x-grid-header">{lockedHeader}</div>',
33248                       '<div class="x-grid-body">{lockedBody}</div>',
33249                   "</div>",
33250                   '<div class="x-grid-viewport">',
33251                       '<div class="x-grid-header">{header}</div>',
33252                       '<div class="x-grid-body">{body}</div>',
33253                   "</div>",
33254                   '<div class="x-grid-bottombar"></div>',
33255                  
33256                   '<div class="x-grid-resize-proxy">&#160;</div>',
33257                "</div>"
33258             );
33259             tpls.master.disableformats = true;
33260         }
33261
33262         if(!tpls.header){
33263             tpls.header = new Roo.Template(
33264                '<table border="0" cellspacing="0" cellpadding="0">',
33265                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33266                "</table>{splits}"
33267             );
33268             tpls.header.disableformats = true;
33269         }
33270         tpls.header.compile();
33271
33272         if(!tpls.hcell){
33273             tpls.hcell = new Roo.Template(
33274                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33275                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33276                 "</div></td>"
33277              );
33278              tpls.hcell.disableFormats = true;
33279         }
33280         tpls.hcell.compile();
33281
33282         if(!tpls.hsplit){
33283             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33284                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33285             tpls.hsplit.disableFormats = true;
33286         }
33287         tpls.hsplit.compile();
33288
33289         if(!tpls.body){
33290             tpls.body = new Roo.Template(
33291                '<table border="0" cellspacing="0" cellpadding="0">',
33292                "<tbody>{rows}</tbody>",
33293                "</table>"
33294             );
33295             tpls.body.disableFormats = true;
33296         }
33297         tpls.body.compile();
33298
33299         if(!tpls.row){
33300             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33301             tpls.row.disableFormats = true;
33302         }
33303         tpls.row.compile();
33304
33305         if(!tpls.cell){
33306             tpls.cell = new Roo.Template(
33307                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33308                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33309                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33310                 "</td>"
33311             );
33312             tpls.cell.disableFormats = true;
33313         }
33314         tpls.cell.compile();
33315
33316         this.templates = tpls;
33317     },
33318
33319     // remap these for backwards compat
33320     onColWidthChange : function(){
33321         this.updateColumns.apply(this, arguments);
33322     },
33323     onHeaderChange : function(){
33324         this.updateHeaders.apply(this, arguments);
33325     }, 
33326     onHiddenChange : function(){
33327         this.handleHiddenChange.apply(this, arguments);
33328     },
33329     onColumnMove : function(){
33330         this.handleColumnMove.apply(this, arguments);
33331     },
33332     onColumnLock : function(){
33333         this.handleLockChange.apply(this, arguments);
33334     },
33335
33336     onDataChange : function(){
33337         this.refresh();
33338         this.updateHeaderSortState();
33339     },
33340
33341     onClear : function(){
33342         this.refresh();
33343     },
33344
33345     onUpdate : function(ds, record){
33346         this.refreshRow(record);
33347     },
33348
33349     refreshRow : function(record){
33350         var ds = this.ds, index;
33351         if(typeof record == 'number'){
33352             index = record;
33353             record = ds.getAt(index);
33354         }else{
33355             index = ds.indexOf(record);
33356         }
33357         this.insertRows(ds, index, index, true);
33358         this.onRemove(ds, record, index+1, true);
33359         this.syncRowHeights(index, index);
33360         this.layout();
33361         this.fireEvent("rowupdated", this, index, record);
33362     },
33363
33364     onAdd : function(ds, records, index){
33365         this.insertRows(ds, index, index + (records.length-1));
33366     },
33367
33368     onRemove : function(ds, record, index, isUpdate){
33369         if(isUpdate !== true){
33370             this.fireEvent("beforerowremoved", this, index, record);
33371         }
33372         var bt = this.getBodyTable(), lt = this.getLockedTable();
33373         if(bt.rows[index]){
33374             bt.firstChild.removeChild(bt.rows[index]);
33375         }
33376         if(lt.rows[index]){
33377             lt.firstChild.removeChild(lt.rows[index]);
33378         }
33379         if(isUpdate !== true){
33380             this.stripeRows(index);
33381             this.syncRowHeights(index, index);
33382             this.layout();
33383             this.fireEvent("rowremoved", this, index, record);
33384         }
33385     },
33386
33387     onLoad : function(){
33388         this.scrollToTop();
33389     },
33390
33391     /**
33392      * Scrolls the grid to the top
33393      */
33394     scrollToTop : function(){
33395         if(this.scroller){
33396             this.scroller.dom.scrollTop = 0;
33397             this.syncScroll();
33398         }
33399     },
33400
33401     /**
33402      * Gets a panel in the header of the grid that can be used for toolbars etc.
33403      * After modifying the contents of this panel a call to grid.autoSize() may be
33404      * required to register any changes in size.
33405      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33406      * @return Roo.Element
33407      */
33408     getHeaderPanel : function(doShow){
33409         if(doShow){
33410             this.headerPanel.show();
33411         }
33412         return this.headerPanel;
33413     },
33414
33415     /**
33416      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33417      * After modifying the contents of this panel a call to grid.autoSize() may be
33418      * required to register any changes in size.
33419      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33420      * @return Roo.Element
33421      */
33422     getFooterPanel : function(doShow){
33423         if(doShow){
33424             this.footerPanel.show();
33425         }
33426         return this.footerPanel;
33427     },
33428
33429     initElements : function(){
33430         var E = Roo.Element;
33431         var el = this.grid.getGridEl().dom.firstChild;
33432         var cs = el.childNodes;
33433
33434         this.el = new E(el);
33435         
33436          this.focusEl = new E(el.firstChild);
33437         this.focusEl.swallowEvent("click", true);
33438         
33439         this.headerPanel = new E(cs[1]);
33440         this.headerPanel.enableDisplayMode("block");
33441
33442         this.scroller = new E(cs[2]);
33443         this.scrollSizer = new E(this.scroller.dom.firstChild);
33444
33445         this.lockedWrap = new E(cs[3]);
33446         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33447         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33448
33449         this.mainWrap = new E(cs[4]);
33450         this.mainHd = new E(this.mainWrap.dom.firstChild);
33451         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33452
33453         this.footerPanel = new E(cs[5]);
33454         this.footerPanel.enableDisplayMode("block");
33455
33456         this.resizeProxy = new E(cs[6]);
33457
33458         this.headerSelector = String.format(
33459            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33460            this.lockedHd.id, this.mainHd.id
33461         );
33462
33463         this.splitterSelector = String.format(
33464            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33465            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33466         );
33467     },
33468     idToCssName : function(s)
33469     {
33470         return s.replace(/[^a-z0-9]+/ig, '-');
33471     },
33472
33473     getHeaderCell : function(index){
33474         return Roo.DomQuery.select(this.headerSelector)[index];
33475     },
33476
33477     getHeaderCellMeasure : function(index){
33478         return this.getHeaderCell(index).firstChild;
33479     },
33480
33481     getHeaderCellText : function(index){
33482         return this.getHeaderCell(index).firstChild.firstChild;
33483     },
33484
33485     getLockedTable : function(){
33486         return this.lockedBody.dom.firstChild;
33487     },
33488
33489     getBodyTable : function(){
33490         return this.mainBody.dom.firstChild;
33491     },
33492
33493     getLockedRow : function(index){
33494         return this.getLockedTable().rows[index];
33495     },
33496
33497     getRow : function(index){
33498         return this.getBodyTable().rows[index];
33499     },
33500
33501     getRowComposite : function(index){
33502         if(!this.rowEl){
33503             this.rowEl = new Roo.CompositeElementLite();
33504         }
33505         var els = [], lrow, mrow;
33506         if(lrow = this.getLockedRow(index)){
33507             els.push(lrow);
33508         }
33509         if(mrow = this.getRow(index)){
33510             els.push(mrow);
33511         }
33512         this.rowEl.elements = els;
33513         return this.rowEl;
33514     },
33515     /**
33516      * Gets the 'td' of the cell
33517      * 
33518      * @param {Integer} rowIndex row to select
33519      * @param {Integer} colIndex column to select
33520      * 
33521      * @return {Object} 
33522      */
33523     getCell : function(rowIndex, colIndex){
33524         var locked = this.cm.getLockedCount();
33525         var source;
33526         if(colIndex < locked){
33527             source = this.lockedBody.dom.firstChild;
33528         }else{
33529             source = this.mainBody.dom.firstChild;
33530             colIndex -= locked;
33531         }
33532         return source.rows[rowIndex].childNodes[colIndex];
33533     },
33534
33535     getCellText : function(rowIndex, colIndex){
33536         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33537     },
33538
33539     getCellBox : function(cell){
33540         var b = this.fly(cell).getBox();
33541         if(Roo.isOpera){ // opera fails to report the Y
33542             b.y = cell.offsetTop + this.mainBody.getY();
33543         }
33544         return b;
33545     },
33546
33547     getCellIndex : function(cell){
33548         var id = String(cell.className).match(this.cellRE);
33549         if(id){
33550             return parseInt(id[1], 10);
33551         }
33552         return 0;
33553     },
33554
33555     findHeaderIndex : function(n){
33556         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33557         return r ? this.getCellIndex(r) : false;
33558     },
33559
33560     findHeaderCell : function(n){
33561         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33562         return r ? r : false;
33563     },
33564
33565     findRowIndex : function(n){
33566         if(!n){
33567             return false;
33568         }
33569         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33570         return r ? r.rowIndex : false;
33571     },
33572
33573     findCellIndex : function(node){
33574         var stop = this.el.dom;
33575         while(node && node != stop){
33576             if(this.findRE.test(node.className)){
33577                 return this.getCellIndex(node);
33578             }
33579             node = node.parentNode;
33580         }
33581         return false;
33582     },
33583
33584     getColumnId : function(index){
33585         return this.cm.getColumnId(index);
33586     },
33587
33588     getSplitters : function()
33589     {
33590         if(this.splitterSelector){
33591            return Roo.DomQuery.select(this.splitterSelector);
33592         }else{
33593             return null;
33594       }
33595     },
33596
33597     getSplitter : function(index){
33598         return this.getSplitters()[index];
33599     },
33600
33601     onRowOver : function(e, t){
33602         var row;
33603         if((row = this.findRowIndex(t)) !== false){
33604             this.getRowComposite(row).addClass("x-grid-row-over");
33605         }
33606     },
33607
33608     onRowOut : function(e, t){
33609         var row;
33610         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33611             this.getRowComposite(row).removeClass("x-grid-row-over");
33612         }
33613     },
33614
33615     renderHeaders : function(){
33616         var cm = this.cm;
33617         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33618         var cb = [], lb = [], sb = [], lsb = [], p = {};
33619         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33620             p.cellId = "x-grid-hd-0-" + i;
33621             p.splitId = "x-grid-csplit-0-" + i;
33622             p.id = cm.getColumnId(i);
33623             p.value = cm.getColumnHeader(i) || "";
33624             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33625             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33626             if(!cm.isLocked(i)){
33627                 cb[cb.length] = ct.apply(p);
33628                 sb[sb.length] = st.apply(p);
33629             }else{
33630                 lb[lb.length] = ct.apply(p);
33631                 lsb[lsb.length] = st.apply(p);
33632             }
33633         }
33634         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33635                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33636     },
33637
33638     updateHeaders : function(){
33639         var html = this.renderHeaders();
33640         this.lockedHd.update(html[0]);
33641         this.mainHd.update(html[1]);
33642     },
33643
33644     /**
33645      * Focuses the specified row.
33646      * @param {Number} row The row index
33647      */
33648     focusRow : function(row)
33649     {
33650         //Roo.log('GridView.focusRow');
33651         var x = this.scroller.dom.scrollLeft;
33652         this.focusCell(row, 0, false);
33653         this.scroller.dom.scrollLeft = x;
33654     },
33655
33656     /**
33657      * Focuses the specified cell.
33658      * @param {Number} row The row index
33659      * @param {Number} col The column index
33660      * @param {Boolean} hscroll false to disable horizontal scrolling
33661      */
33662     focusCell : function(row, col, hscroll)
33663     {
33664         //Roo.log('GridView.focusCell');
33665         var el = this.ensureVisible(row, col, hscroll);
33666         this.focusEl.alignTo(el, "tl-tl");
33667         if(Roo.isGecko){
33668             this.focusEl.focus();
33669         }else{
33670             this.focusEl.focus.defer(1, this.focusEl);
33671         }
33672     },
33673
33674     /**
33675      * Scrolls the specified cell into view
33676      * @param {Number} row The row index
33677      * @param {Number} col The column index
33678      * @param {Boolean} hscroll false to disable horizontal scrolling
33679      */
33680     ensureVisible : function(row, col, hscroll)
33681     {
33682         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33683         //return null; //disable for testing.
33684         if(typeof row != "number"){
33685             row = row.rowIndex;
33686         }
33687         if(row < 0 && row >= this.ds.getCount()){
33688             return  null;
33689         }
33690         col = (col !== undefined ? col : 0);
33691         var cm = this.grid.colModel;
33692         while(cm.isHidden(col)){
33693             col++;
33694         }
33695
33696         var el = this.getCell(row, col);
33697         if(!el){
33698             return null;
33699         }
33700         var c = this.scroller.dom;
33701
33702         var ctop = parseInt(el.offsetTop, 10);
33703         var cleft = parseInt(el.offsetLeft, 10);
33704         var cbot = ctop + el.offsetHeight;
33705         var cright = cleft + el.offsetWidth;
33706         
33707         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33708         var stop = parseInt(c.scrollTop, 10);
33709         var sleft = parseInt(c.scrollLeft, 10);
33710         var sbot = stop + ch;
33711         var sright = sleft + c.clientWidth;
33712         /*
33713         Roo.log('GridView.ensureVisible:' +
33714                 ' ctop:' + ctop +
33715                 ' c.clientHeight:' + c.clientHeight +
33716                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33717                 ' stop:' + stop +
33718                 ' cbot:' + cbot +
33719                 ' sbot:' + sbot +
33720                 ' ch:' + ch  
33721                 );
33722         */
33723         if(ctop < stop){
33724              c.scrollTop = ctop;
33725             //Roo.log("set scrolltop to ctop DISABLE?");
33726         }else if(cbot > sbot){
33727             //Roo.log("set scrolltop to cbot-ch");
33728             c.scrollTop = cbot-ch;
33729         }
33730         
33731         if(hscroll !== false){
33732             if(cleft < sleft){
33733                 c.scrollLeft = cleft;
33734             }else if(cright > sright){
33735                 c.scrollLeft = cright-c.clientWidth;
33736             }
33737         }
33738          
33739         return el;
33740     },
33741
33742     updateColumns : function(){
33743         this.grid.stopEditing();
33744         var cm = this.grid.colModel, colIds = this.getColumnIds();
33745         //var totalWidth = cm.getTotalWidth();
33746         var pos = 0;
33747         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33748             //if(cm.isHidden(i)) continue;
33749             var w = cm.getColumnWidth(i);
33750             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33751             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33752         }
33753         this.updateSplitters();
33754     },
33755
33756     generateRules : function(cm){
33757         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33758         Roo.util.CSS.removeStyleSheet(rulesId);
33759         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33760             var cid = cm.getColumnId(i);
33761             var align = '';
33762             if(cm.config[i].align){
33763                 align = 'text-align:'+cm.config[i].align+';';
33764             }
33765             var hidden = '';
33766             if(cm.isHidden(i)){
33767                 hidden = 'display:none;';
33768             }
33769             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33770             ruleBuf.push(
33771                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33772                     this.hdSelector, cid, " {\n", align, width, "}\n",
33773                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33774                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33775         }
33776         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33777     },
33778
33779     updateSplitters : function(){
33780         var cm = this.cm, s = this.getSplitters();
33781         if(s){ // splitters not created yet
33782             var pos = 0, locked = true;
33783             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33784                 if(cm.isHidden(i)) {
33785                     continue;
33786                 }
33787                 var w = cm.getColumnWidth(i); // make sure it's a number
33788                 if(!cm.isLocked(i) && locked){
33789                     pos = 0;
33790                     locked = false;
33791                 }
33792                 pos += w;
33793                 s[i].style.left = (pos-this.splitOffset) + "px";
33794             }
33795         }
33796     },
33797
33798     handleHiddenChange : function(colModel, colIndex, hidden){
33799         if(hidden){
33800             this.hideColumn(colIndex);
33801         }else{
33802             this.unhideColumn(colIndex);
33803         }
33804     },
33805
33806     hideColumn : function(colIndex){
33807         var cid = this.getColumnId(colIndex);
33808         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33809         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33810         if(Roo.isSafari){
33811             this.updateHeaders();
33812         }
33813         this.updateSplitters();
33814         this.layout();
33815     },
33816
33817     unhideColumn : function(colIndex){
33818         var cid = this.getColumnId(colIndex);
33819         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33820         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33821
33822         if(Roo.isSafari){
33823             this.updateHeaders();
33824         }
33825         this.updateSplitters();
33826         this.layout();
33827     },
33828
33829     insertRows : function(dm, firstRow, lastRow, isUpdate){
33830         if(firstRow == 0 && lastRow == dm.getCount()-1){
33831             this.refresh();
33832         }else{
33833             if(!isUpdate){
33834                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33835             }
33836             var s = this.getScrollState();
33837             var markup = this.renderRows(firstRow, lastRow);
33838             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33839             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33840             this.restoreScroll(s);
33841             if(!isUpdate){
33842                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33843                 this.syncRowHeights(firstRow, lastRow);
33844                 this.stripeRows(firstRow);
33845                 this.layout();
33846             }
33847         }
33848     },
33849
33850     bufferRows : function(markup, target, index){
33851         var before = null, trows = target.rows, tbody = target.tBodies[0];
33852         if(index < trows.length){
33853             before = trows[index];
33854         }
33855         var b = document.createElement("div");
33856         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33857         var rows = b.firstChild.rows;
33858         for(var i = 0, len = rows.length; i < len; i++){
33859             if(before){
33860                 tbody.insertBefore(rows[0], before);
33861             }else{
33862                 tbody.appendChild(rows[0]);
33863             }
33864         }
33865         b.innerHTML = "";
33866         b = null;
33867     },
33868
33869     deleteRows : function(dm, firstRow, lastRow){
33870         if(dm.getRowCount()<1){
33871             this.fireEvent("beforerefresh", this);
33872             this.mainBody.update("");
33873             this.lockedBody.update("");
33874             this.fireEvent("refresh", this);
33875         }else{
33876             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33877             var bt = this.getBodyTable();
33878             var tbody = bt.firstChild;
33879             var rows = bt.rows;
33880             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33881                 tbody.removeChild(rows[firstRow]);
33882             }
33883             this.stripeRows(firstRow);
33884             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33885         }
33886     },
33887
33888     updateRows : function(dataSource, firstRow, lastRow){
33889         var s = this.getScrollState();
33890         this.refresh();
33891         this.restoreScroll(s);
33892     },
33893
33894     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33895         if(!noRefresh){
33896            this.refresh();
33897         }
33898         this.updateHeaderSortState();
33899     },
33900
33901     getScrollState : function(){
33902         
33903         var sb = this.scroller.dom;
33904         return {left: sb.scrollLeft, top: sb.scrollTop};
33905     },
33906
33907     stripeRows : function(startRow){
33908         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33909             return;
33910         }
33911         startRow = startRow || 0;
33912         var rows = this.getBodyTable().rows;
33913         var lrows = this.getLockedTable().rows;
33914         var cls = ' x-grid-row-alt ';
33915         for(var i = startRow, len = rows.length; i < len; i++){
33916             var row = rows[i], lrow = lrows[i];
33917             var isAlt = ((i+1) % 2 == 0);
33918             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33919             if(isAlt == hasAlt){
33920                 continue;
33921             }
33922             if(isAlt){
33923                 row.className += " x-grid-row-alt";
33924             }else{
33925                 row.className = row.className.replace("x-grid-row-alt", "");
33926             }
33927             if(lrow){
33928                 lrow.className = row.className;
33929             }
33930         }
33931     },
33932
33933     restoreScroll : function(state){
33934         //Roo.log('GridView.restoreScroll');
33935         var sb = this.scroller.dom;
33936         sb.scrollLeft = state.left;
33937         sb.scrollTop = state.top;
33938         this.syncScroll();
33939     },
33940
33941     syncScroll : function(){
33942         //Roo.log('GridView.syncScroll');
33943         var sb = this.scroller.dom;
33944         var sh = this.mainHd.dom;
33945         var bs = this.mainBody.dom;
33946         var lv = this.lockedBody.dom;
33947         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33948         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33949     },
33950
33951     handleScroll : function(e){
33952         this.syncScroll();
33953         var sb = this.scroller.dom;
33954         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33955         e.stopEvent();
33956     },
33957
33958     handleWheel : function(e){
33959         var d = e.getWheelDelta();
33960         this.scroller.dom.scrollTop -= d*22;
33961         // set this here to prevent jumpy scrolling on large tables
33962         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33963         e.stopEvent();
33964     },
33965
33966     renderRows : function(startRow, endRow){
33967         // pull in all the crap needed to render rows
33968         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33969         var colCount = cm.getColumnCount();
33970
33971         if(ds.getCount() < 1){
33972             return ["", ""];
33973         }
33974
33975         // build a map for all the columns
33976         var cs = [];
33977         for(var i = 0; i < colCount; i++){
33978             var name = cm.getDataIndex(i);
33979             cs[i] = {
33980                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33981                 renderer : cm.getRenderer(i),
33982                 id : cm.getColumnId(i),
33983                 locked : cm.isLocked(i),
33984                 has_editor : cm.isCellEditable(i)
33985             };
33986         }
33987
33988         startRow = startRow || 0;
33989         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
33990
33991         // records to render
33992         var rs = ds.getRange(startRow, endRow);
33993
33994         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
33995     },
33996
33997     // As much as I hate to duplicate code, this was branched because FireFox really hates
33998     // [].join("") on strings. The performance difference was substantial enough to
33999     // branch this function
34000     doRender : Roo.isGecko ?
34001             function(cs, rs, ds, startRow, colCount, stripe){
34002                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34003                 // buffers
34004                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34005                 
34006                 var hasListener = this.grid.hasListener('rowclass');
34007                 var rowcfg = {};
34008                 for(var j = 0, len = rs.length; j < len; j++){
34009                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34010                     for(var i = 0; i < colCount; i++){
34011                         c = cs[i];
34012                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34013                         p.id = c.id;
34014                         p.css = p.attr = "";
34015                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34016                         if(p.value == undefined || p.value === "") {
34017                             p.value = "&#160;";
34018                         }
34019                         if(c.has_editor){
34020                             p.css += ' x-grid-editable-cell';
34021                         }
34022                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34023                             p.css +=  ' x-grid-dirty-cell';
34024                         }
34025                         var markup = ct.apply(p);
34026                         if(!c.locked){
34027                             cb+= markup;
34028                         }else{
34029                             lcb+= markup;
34030                         }
34031                     }
34032                     var alt = [];
34033                     if(stripe && ((rowIndex+1) % 2 == 0)){
34034                         alt.push("x-grid-row-alt")
34035                     }
34036                     if(r.dirty){
34037                         alt.push(  " x-grid-dirty-row");
34038                     }
34039                     rp.cells = lcb;
34040                     if(this.getRowClass){
34041                         alt.push(this.getRowClass(r, rowIndex));
34042                     }
34043                     if (hasListener) {
34044                         rowcfg = {
34045                              
34046                             record: r,
34047                             rowIndex : rowIndex,
34048                             rowClass : ''
34049                         };
34050                         this.grid.fireEvent('rowclass', this, rowcfg);
34051                         alt.push(rowcfg.rowClass);
34052                     }
34053                     rp.alt = alt.join(" ");
34054                     lbuf+= rt.apply(rp);
34055                     rp.cells = cb;
34056                     buf+=  rt.apply(rp);
34057                 }
34058                 return [lbuf, buf];
34059             } :
34060             function(cs, rs, ds, startRow, colCount, stripe){
34061                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34062                 // buffers
34063                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34064                 var hasListener = this.grid.hasListener('rowclass');
34065  
34066                 var rowcfg = {};
34067                 for(var j = 0, len = rs.length; j < len; j++){
34068                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34069                     for(var i = 0; i < colCount; i++){
34070                         c = cs[i];
34071                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34072                         p.id = c.id;
34073                         p.css = p.attr = "";
34074                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34075                         if(p.value == undefined || p.value === "") {
34076                             p.value = "&#160;";
34077                         }
34078                         //Roo.log(c);
34079                          if(c.has_editor){
34080                             p.css += ' x-grid-editable-cell';
34081                         }
34082                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34083                             p.css += ' x-grid-dirty-cell' 
34084                         }
34085                         
34086                         var markup = ct.apply(p);
34087                         if(!c.locked){
34088                             cb[cb.length] = markup;
34089                         }else{
34090                             lcb[lcb.length] = markup;
34091                         }
34092                     }
34093                     var alt = [];
34094                     if(stripe && ((rowIndex+1) % 2 == 0)){
34095                         alt.push( "x-grid-row-alt");
34096                     }
34097                     if(r.dirty){
34098                         alt.push(" x-grid-dirty-row");
34099                     }
34100                     rp.cells = lcb;
34101                     if(this.getRowClass){
34102                         alt.push( this.getRowClass(r, rowIndex));
34103                     }
34104                     if (hasListener) {
34105                         rowcfg = {
34106                              
34107                             record: r,
34108                             rowIndex : rowIndex,
34109                             rowClass : ''
34110                         };
34111                         this.grid.fireEvent('rowclass', this, rowcfg);
34112                         alt.push(rowcfg.rowClass);
34113                     }
34114                     
34115                     rp.alt = alt.join(" ");
34116                     rp.cells = lcb.join("");
34117                     lbuf[lbuf.length] = rt.apply(rp);
34118                     rp.cells = cb.join("");
34119                     buf[buf.length] =  rt.apply(rp);
34120                 }
34121                 return [lbuf.join(""), buf.join("")];
34122             },
34123
34124     renderBody : function(){
34125         var markup = this.renderRows();
34126         var bt = this.templates.body;
34127         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34128     },
34129
34130     /**
34131      * Refreshes the grid
34132      * @param {Boolean} headersToo
34133      */
34134     refresh : function(headersToo){
34135         this.fireEvent("beforerefresh", this);
34136         this.grid.stopEditing();
34137         var result = this.renderBody();
34138         this.lockedBody.update(result[0]);
34139         this.mainBody.update(result[1]);
34140         if(headersToo === true){
34141             this.updateHeaders();
34142             this.updateColumns();
34143             this.updateSplitters();
34144             this.updateHeaderSortState();
34145         }
34146         this.syncRowHeights();
34147         this.layout();
34148         this.fireEvent("refresh", this);
34149     },
34150
34151     handleColumnMove : function(cm, oldIndex, newIndex){
34152         this.indexMap = null;
34153         var s = this.getScrollState();
34154         this.refresh(true);
34155         this.restoreScroll(s);
34156         this.afterMove(newIndex);
34157     },
34158
34159     afterMove : function(colIndex){
34160         if(this.enableMoveAnim && Roo.enableFx){
34161             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34162         }
34163         // if multisort - fix sortOrder, and reload..
34164         if (this.grid.dataSource.multiSort) {
34165             // the we can call sort again..
34166             var dm = this.grid.dataSource;
34167             var cm = this.grid.colModel;
34168             var so = [];
34169             for(var i = 0; i < cm.config.length; i++ ) {
34170                 
34171                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34172                     continue; // dont' bother, it's not in sort list or being set.
34173                 }
34174                 
34175                 so.push(cm.config[i].dataIndex);
34176             };
34177             dm.sortOrder = so;
34178             dm.load(dm.lastOptions);
34179             
34180             
34181         }
34182         
34183     },
34184
34185     updateCell : function(dm, rowIndex, dataIndex){
34186         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34187         if(typeof colIndex == "undefined"){ // not present in grid
34188             return;
34189         }
34190         var cm = this.grid.colModel;
34191         var cell = this.getCell(rowIndex, colIndex);
34192         var cellText = this.getCellText(rowIndex, colIndex);
34193
34194         var p = {
34195             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34196             id : cm.getColumnId(colIndex),
34197             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34198         };
34199         var renderer = cm.getRenderer(colIndex);
34200         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34201         if(typeof val == "undefined" || val === "") {
34202             val = "&#160;";
34203         }
34204         cellText.innerHTML = val;
34205         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34206         this.syncRowHeights(rowIndex, rowIndex);
34207     },
34208
34209     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34210         var maxWidth = 0;
34211         if(this.grid.autoSizeHeaders){
34212             var h = this.getHeaderCellMeasure(colIndex);
34213             maxWidth = Math.max(maxWidth, h.scrollWidth);
34214         }
34215         var tb, index;
34216         if(this.cm.isLocked(colIndex)){
34217             tb = this.getLockedTable();
34218             index = colIndex;
34219         }else{
34220             tb = this.getBodyTable();
34221             index = colIndex - this.cm.getLockedCount();
34222         }
34223         if(tb && tb.rows){
34224             var rows = tb.rows;
34225             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34226             for(var i = 0; i < stopIndex; i++){
34227                 var cell = rows[i].childNodes[index].firstChild;
34228                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34229             }
34230         }
34231         return maxWidth + /*margin for error in IE*/ 5;
34232     },
34233     /**
34234      * Autofit a column to its content.
34235      * @param {Number} colIndex
34236      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34237      */
34238      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34239          if(this.cm.isHidden(colIndex)){
34240              return; // can't calc a hidden column
34241          }
34242         if(forceMinSize){
34243             var cid = this.cm.getColumnId(colIndex);
34244             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34245            if(this.grid.autoSizeHeaders){
34246                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34247            }
34248         }
34249         var newWidth = this.calcColumnWidth(colIndex);
34250         this.cm.setColumnWidth(colIndex,
34251             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34252         if(!suppressEvent){
34253             this.grid.fireEvent("columnresize", colIndex, newWidth);
34254         }
34255     },
34256
34257     /**
34258      * Autofits all columns to their content and then expands to fit any extra space in the grid
34259      */
34260      autoSizeColumns : function(){
34261         var cm = this.grid.colModel;
34262         var colCount = cm.getColumnCount();
34263         for(var i = 0; i < colCount; i++){
34264             this.autoSizeColumn(i, true, true);
34265         }
34266         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34267             this.fitColumns();
34268         }else{
34269             this.updateColumns();
34270             this.layout();
34271         }
34272     },
34273
34274     /**
34275      * Autofits all columns to the grid's width proportionate with their current size
34276      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34277      */
34278     fitColumns : function(reserveScrollSpace){
34279         var cm = this.grid.colModel;
34280         var colCount = cm.getColumnCount();
34281         var cols = [];
34282         var width = 0;
34283         var i, w;
34284         for (i = 0; i < colCount; i++){
34285             if(!cm.isHidden(i) && !cm.isFixed(i)){
34286                 w = cm.getColumnWidth(i);
34287                 cols.push(i);
34288                 cols.push(w);
34289                 width += w;
34290             }
34291         }
34292         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34293         if(reserveScrollSpace){
34294             avail -= 17;
34295         }
34296         var frac = (avail - cm.getTotalWidth())/width;
34297         while (cols.length){
34298             w = cols.pop();
34299             i = cols.pop();
34300             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34301         }
34302         this.updateColumns();
34303         this.layout();
34304     },
34305
34306     onRowSelect : function(rowIndex){
34307         var row = this.getRowComposite(rowIndex);
34308         row.addClass("x-grid-row-selected");
34309     },
34310
34311     onRowDeselect : function(rowIndex){
34312         var row = this.getRowComposite(rowIndex);
34313         row.removeClass("x-grid-row-selected");
34314     },
34315
34316     onCellSelect : function(row, col){
34317         var cell = this.getCell(row, col);
34318         if(cell){
34319             Roo.fly(cell).addClass("x-grid-cell-selected");
34320         }
34321     },
34322
34323     onCellDeselect : function(row, col){
34324         var cell = this.getCell(row, col);
34325         if(cell){
34326             Roo.fly(cell).removeClass("x-grid-cell-selected");
34327         }
34328     },
34329
34330     updateHeaderSortState : function(){
34331         
34332         // sort state can be single { field: xxx, direction : yyy}
34333         // or   { xxx=>ASC , yyy : DESC ..... }
34334         
34335         var mstate = {};
34336         if (!this.ds.multiSort) { 
34337             var state = this.ds.getSortState();
34338             if(!state){
34339                 return;
34340             }
34341             mstate[state.field] = state.direction;
34342             // FIXME... - this is not used here.. but might be elsewhere..
34343             this.sortState = state;
34344             
34345         } else {
34346             mstate = this.ds.sortToggle;
34347         }
34348         //remove existing sort classes..
34349         
34350         var sc = this.sortClasses;
34351         var hds = this.el.select(this.headerSelector).removeClass(sc);
34352         
34353         for(var f in mstate) {
34354         
34355             var sortColumn = this.cm.findColumnIndex(f);
34356             
34357             if(sortColumn != -1){
34358                 var sortDir = mstate[f];        
34359                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34360             }
34361         }
34362         
34363          
34364         
34365     },
34366
34367
34368     handleHeaderClick : function(g, index,e){
34369         
34370         Roo.log("header click");
34371         
34372         if (Roo.isTouch) {
34373             // touch events on header are handled by context
34374             this.handleHdCtx(g,index,e);
34375             return;
34376         }
34377         
34378         
34379         if(this.headersDisabled){
34380             return;
34381         }
34382         var dm = g.dataSource, cm = g.colModel;
34383         if(!cm.isSortable(index)){
34384             return;
34385         }
34386         g.stopEditing();
34387         
34388         if (dm.multiSort) {
34389             // update the sortOrder
34390             var so = [];
34391             for(var i = 0; i < cm.config.length; i++ ) {
34392                 
34393                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34394                     continue; // dont' bother, it's not in sort list or being set.
34395                 }
34396                 
34397                 so.push(cm.config[i].dataIndex);
34398             };
34399             dm.sortOrder = so;
34400         }
34401         
34402         
34403         dm.sort(cm.getDataIndex(index));
34404     },
34405
34406
34407     destroy : function(){
34408         if(this.colMenu){
34409             this.colMenu.removeAll();
34410             Roo.menu.MenuMgr.unregister(this.colMenu);
34411             this.colMenu.getEl().remove();
34412             delete this.colMenu;
34413         }
34414         if(this.hmenu){
34415             this.hmenu.removeAll();
34416             Roo.menu.MenuMgr.unregister(this.hmenu);
34417             this.hmenu.getEl().remove();
34418             delete this.hmenu;
34419         }
34420         if(this.grid.enableColumnMove){
34421             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34422             if(dds){
34423                 for(var dd in dds){
34424                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34425                         var elid = dds[dd].dragElId;
34426                         dds[dd].unreg();
34427                         Roo.get(elid).remove();
34428                     } else if(dds[dd].config.isTarget){
34429                         dds[dd].proxyTop.remove();
34430                         dds[dd].proxyBottom.remove();
34431                         dds[dd].unreg();
34432                     }
34433                     if(Roo.dd.DDM.locationCache[dd]){
34434                         delete Roo.dd.DDM.locationCache[dd];
34435                     }
34436                 }
34437                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34438             }
34439         }
34440         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34441         this.bind(null, null);
34442         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34443     },
34444
34445     handleLockChange : function(){
34446         this.refresh(true);
34447     },
34448
34449     onDenyColumnLock : function(){
34450
34451     },
34452
34453     onDenyColumnHide : function(){
34454
34455     },
34456
34457     handleHdMenuClick : function(item){
34458         var index = this.hdCtxIndex;
34459         var cm = this.cm, ds = this.ds;
34460         switch(item.id){
34461             case "asc":
34462                 ds.sort(cm.getDataIndex(index), "ASC");
34463                 break;
34464             case "desc":
34465                 ds.sort(cm.getDataIndex(index), "DESC");
34466                 break;
34467             case "lock":
34468                 var lc = cm.getLockedCount();
34469                 if(cm.getColumnCount(true) <= lc+1){
34470                     this.onDenyColumnLock();
34471                     return;
34472                 }
34473                 if(lc != index){
34474                     cm.setLocked(index, true, true);
34475                     cm.moveColumn(index, lc);
34476                     this.grid.fireEvent("columnmove", index, lc);
34477                 }else{
34478                     cm.setLocked(index, true);
34479                 }
34480             break;
34481             case "unlock":
34482                 var lc = cm.getLockedCount();
34483                 if((lc-1) != index){
34484                     cm.setLocked(index, false, true);
34485                     cm.moveColumn(index, lc-1);
34486                     this.grid.fireEvent("columnmove", index, lc-1);
34487                 }else{
34488                     cm.setLocked(index, false);
34489                 }
34490             break;
34491             case 'wider': // used to expand cols on touch..
34492             case 'narrow':
34493                 var cw = cm.getColumnWidth(index);
34494                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34495                 cw = Math.max(0, cw);
34496                 cw = Math.min(cw,4000);
34497                 cm.setColumnWidth(index, cw);
34498                 break;
34499                 
34500             default:
34501                 index = cm.getIndexById(item.id.substr(4));
34502                 if(index != -1){
34503                     if(item.checked && cm.getColumnCount(true) <= 1){
34504                         this.onDenyColumnHide();
34505                         return false;
34506                     }
34507                     cm.setHidden(index, item.checked);
34508                 }
34509         }
34510         return true;
34511     },
34512
34513     beforeColMenuShow : function(){
34514         var cm = this.cm,  colCount = cm.getColumnCount();
34515         this.colMenu.removeAll();
34516         for(var i = 0; i < colCount; i++){
34517             this.colMenu.add(new Roo.menu.CheckItem({
34518                 id: "col-"+cm.getColumnId(i),
34519                 text: cm.getColumnHeader(i),
34520                 checked: !cm.isHidden(i),
34521                 hideOnClick:false
34522             }));
34523         }
34524     },
34525
34526     handleHdCtx : function(g, index, e){
34527         e.stopEvent();
34528         var hd = this.getHeaderCell(index);
34529         this.hdCtxIndex = index;
34530         var ms = this.hmenu.items, cm = this.cm;
34531         ms.get("asc").setDisabled(!cm.isSortable(index));
34532         ms.get("desc").setDisabled(!cm.isSortable(index));
34533         if(this.grid.enableColLock !== false){
34534             ms.get("lock").setDisabled(cm.isLocked(index));
34535             ms.get("unlock").setDisabled(!cm.isLocked(index));
34536         }
34537         this.hmenu.show(hd, "tl-bl");
34538     },
34539
34540     handleHdOver : function(e){
34541         var hd = this.findHeaderCell(e.getTarget());
34542         if(hd && !this.headersDisabled){
34543             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34544                this.fly(hd).addClass("x-grid-hd-over");
34545             }
34546         }
34547     },
34548
34549     handleHdOut : function(e){
34550         var hd = this.findHeaderCell(e.getTarget());
34551         if(hd){
34552             this.fly(hd).removeClass("x-grid-hd-over");
34553         }
34554     },
34555
34556     handleSplitDblClick : function(e, t){
34557         var i = this.getCellIndex(t);
34558         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34559             this.autoSizeColumn(i, true);
34560             this.layout();
34561         }
34562     },
34563
34564     render : function(){
34565
34566         var cm = this.cm;
34567         var colCount = cm.getColumnCount();
34568
34569         if(this.grid.monitorWindowResize === true){
34570             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34571         }
34572         var header = this.renderHeaders();
34573         var body = this.templates.body.apply({rows:""});
34574         var html = this.templates.master.apply({
34575             lockedBody: body,
34576             body: body,
34577             lockedHeader: header[0],
34578             header: header[1]
34579         });
34580
34581         //this.updateColumns();
34582
34583         this.grid.getGridEl().dom.innerHTML = html;
34584
34585         this.initElements();
34586         
34587         // a kludge to fix the random scolling effect in webkit
34588         this.el.on("scroll", function() {
34589             this.el.dom.scrollTop=0; // hopefully not recursive..
34590         },this);
34591
34592         this.scroller.on("scroll", this.handleScroll, this);
34593         this.lockedBody.on("mousewheel", this.handleWheel, this);
34594         this.mainBody.on("mousewheel", this.handleWheel, this);
34595
34596         this.mainHd.on("mouseover", this.handleHdOver, this);
34597         this.mainHd.on("mouseout", this.handleHdOut, this);
34598         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34599                 {delegate: "."+this.splitClass});
34600
34601         this.lockedHd.on("mouseover", this.handleHdOver, this);
34602         this.lockedHd.on("mouseout", this.handleHdOut, this);
34603         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34604                 {delegate: "."+this.splitClass});
34605
34606         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34607             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34608         }
34609
34610         this.updateSplitters();
34611
34612         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34613             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34614             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34615         }
34616
34617         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34618             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34619             this.hmenu.add(
34620                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34621                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34622             );
34623             if(this.grid.enableColLock !== false){
34624                 this.hmenu.add('-',
34625                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34626                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34627                 );
34628             }
34629             if (Roo.isTouch) {
34630                  this.hmenu.add('-',
34631                     {id:"wider", text: this.columnsWiderText},
34632                     {id:"narrow", text: this.columnsNarrowText }
34633                 );
34634                 
34635                  
34636             }
34637             
34638             if(this.grid.enableColumnHide !== false){
34639
34640                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34641                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34642                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34643
34644                 this.hmenu.add('-',
34645                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34646                 );
34647             }
34648             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34649
34650             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34651         }
34652
34653         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34654             this.dd = new Roo.grid.GridDragZone(this.grid, {
34655                 ddGroup : this.grid.ddGroup || 'GridDD'
34656             });
34657             
34658         }
34659
34660         /*
34661         for(var i = 0; i < colCount; i++){
34662             if(cm.isHidden(i)){
34663                 this.hideColumn(i);
34664             }
34665             if(cm.config[i].align){
34666                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34667                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34668             }
34669         }*/
34670         
34671         this.updateHeaderSortState();
34672
34673         this.beforeInitialResize();
34674         this.layout(true);
34675
34676         // two part rendering gives faster view to the user
34677         this.renderPhase2.defer(1, this);
34678     },
34679
34680     renderPhase2 : function(){
34681         // render the rows now
34682         this.refresh();
34683         if(this.grid.autoSizeColumns){
34684             this.autoSizeColumns();
34685         }
34686     },
34687
34688     beforeInitialResize : function(){
34689
34690     },
34691
34692     onColumnSplitterMoved : function(i, w){
34693         this.userResized = true;
34694         var cm = this.grid.colModel;
34695         cm.setColumnWidth(i, w, true);
34696         var cid = cm.getColumnId(i);
34697         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34698         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34699         this.updateSplitters();
34700         this.layout();
34701         this.grid.fireEvent("columnresize", i, w);
34702     },
34703
34704     syncRowHeights : function(startIndex, endIndex){
34705         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34706             startIndex = startIndex || 0;
34707             var mrows = this.getBodyTable().rows;
34708             var lrows = this.getLockedTable().rows;
34709             var len = mrows.length-1;
34710             endIndex = Math.min(endIndex || len, len);
34711             for(var i = startIndex; i <= endIndex; i++){
34712                 var m = mrows[i], l = lrows[i];
34713                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34714                 m.style.height = l.style.height = h + "px";
34715             }
34716         }
34717     },
34718
34719     layout : function(initialRender, is2ndPass){
34720         var g = this.grid;
34721         var auto = g.autoHeight;
34722         var scrollOffset = 16;
34723         var c = g.getGridEl(), cm = this.cm,
34724                 expandCol = g.autoExpandColumn,
34725                 gv = this;
34726         //c.beginMeasure();
34727
34728         if(!c.dom.offsetWidth){ // display:none?
34729             if(initialRender){
34730                 this.lockedWrap.show();
34731                 this.mainWrap.show();
34732             }
34733             return;
34734         }
34735
34736         var hasLock = this.cm.isLocked(0);
34737
34738         var tbh = this.headerPanel.getHeight();
34739         var bbh = this.footerPanel.getHeight();
34740
34741         if(auto){
34742             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34743             var newHeight = ch + c.getBorderWidth("tb");
34744             if(g.maxHeight){
34745                 newHeight = Math.min(g.maxHeight, newHeight);
34746             }
34747             c.setHeight(newHeight);
34748         }
34749
34750         if(g.autoWidth){
34751             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34752         }
34753
34754         var s = this.scroller;
34755
34756         var csize = c.getSize(true);
34757
34758         this.el.setSize(csize.width, csize.height);
34759
34760         this.headerPanel.setWidth(csize.width);
34761         this.footerPanel.setWidth(csize.width);
34762
34763         var hdHeight = this.mainHd.getHeight();
34764         var vw = csize.width;
34765         var vh = csize.height - (tbh + bbh);
34766
34767         s.setSize(vw, vh);
34768
34769         var bt = this.getBodyTable();
34770         
34771         if(cm.getLockedCount() == cm.config.length){
34772             bt = this.getLockedTable();
34773         }
34774         
34775         var ltWidth = hasLock ?
34776                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34777
34778         var scrollHeight = bt.offsetHeight;
34779         var scrollWidth = ltWidth + bt.offsetWidth;
34780         var vscroll = false, hscroll = false;
34781
34782         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34783
34784         var lw = this.lockedWrap, mw = this.mainWrap;
34785         var lb = this.lockedBody, mb = this.mainBody;
34786
34787         setTimeout(function(){
34788             var t = s.dom.offsetTop;
34789             var w = s.dom.clientWidth,
34790                 h = s.dom.clientHeight;
34791
34792             lw.setTop(t);
34793             lw.setSize(ltWidth, h);
34794
34795             mw.setLeftTop(ltWidth, t);
34796             mw.setSize(w-ltWidth, h);
34797
34798             lb.setHeight(h-hdHeight);
34799             mb.setHeight(h-hdHeight);
34800
34801             if(is2ndPass !== true && !gv.userResized && expandCol){
34802                 // high speed resize without full column calculation
34803                 
34804                 var ci = cm.getIndexById(expandCol);
34805                 if (ci < 0) {
34806                     ci = cm.findColumnIndex(expandCol);
34807                 }
34808                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34809                 var expandId = cm.getColumnId(ci);
34810                 var  tw = cm.getTotalWidth(false);
34811                 var currentWidth = cm.getColumnWidth(ci);
34812                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34813                 if(currentWidth != cw){
34814                     cm.setColumnWidth(ci, cw, true);
34815                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34816                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34817                     gv.updateSplitters();
34818                     gv.layout(false, true);
34819                 }
34820             }
34821
34822             if(initialRender){
34823                 lw.show();
34824                 mw.show();
34825             }
34826             //c.endMeasure();
34827         }, 10);
34828     },
34829
34830     onWindowResize : function(){
34831         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34832             return;
34833         }
34834         this.layout();
34835     },
34836
34837     appendFooter : function(parentEl){
34838         return null;
34839     },
34840
34841     sortAscText : "Sort Ascending",
34842     sortDescText : "Sort Descending",
34843     lockText : "Lock Column",
34844     unlockText : "Unlock Column",
34845     columnsText : "Columns",
34846  
34847     columnsWiderText : "Wider",
34848     columnsNarrowText : "Thinner"
34849 });
34850
34851
34852 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34853     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34854     this.proxy.el.addClass('x-grid3-col-dd');
34855 };
34856
34857 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34858     handleMouseDown : function(e){
34859
34860     },
34861
34862     callHandleMouseDown : function(e){
34863         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34864     }
34865 });
34866 /*
34867  * Based on:
34868  * Ext JS Library 1.1.1
34869  * Copyright(c) 2006-2007, Ext JS, LLC.
34870  *
34871  * Originally Released Under LGPL - original licence link has changed is not relivant.
34872  *
34873  * Fork - LGPL
34874  * <script type="text/javascript">
34875  */
34876  
34877 // private
34878 // This is a support class used internally by the Grid components
34879 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34880     this.grid = grid;
34881     this.view = grid.getView();
34882     this.proxy = this.view.resizeProxy;
34883     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34884         "gridSplitters" + this.grid.getGridEl().id, {
34885         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34886     });
34887     this.setHandleElId(Roo.id(hd));
34888     this.setOuterHandleElId(Roo.id(hd2));
34889     this.scroll = false;
34890 };
34891 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34892     fly: Roo.Element.fly,
34893
34894     b4StartDrag : function(x, y){
34895         this.view.headersDisabled = true;
34896         this.proxy.setHeight(this.view.mainWrap.getHeight());
34897         var w = this.cm.getColumnWidth(this.cellIndex);
34898         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34899         this.resetConstraints();
34900         this.setXConstraint(minw, 1000);
34901         this.setYConstraint(0, 0);
34902         this.minX = x - minw;
34903         this.maxX = x + 1000;
34904         this.startPos = x;
34905         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34906     },
34907
34908
34909     handleMouseDown : function(e){
34910         ev = Roo.EventObject.setEvent(e);
34911         var t = this.fly(ev.getTarget());
34912         if(t.hasClass("x-grid-split")){
34913             this.cellIndex = this.view.getCellIndex(t.dom);
34914             this.split = t.dom;
34915             this.cm = this.grid.colModel;
34916             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34917                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34918             }
34919         }
34920     },
34921
34922     endDrag : function(e){
34923         this.view.headersDisabled = false;
34924         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34925         var diff = endX - this.startPos;
34926         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34927     },
34928
34929     autoOffset : function(){
34930         this.setDelta(0,0);
34931     }
34932 });/*
34933  * Based on:
34934  * Ext JS Library 1.1.1
34935  * Copyright(c) 2006-2007, Ext JS, LLC.
34936  *
34937  * Originally Released Under LGPL - original licence link has changed is not relivant.
34938  *
34939  * Fork - LGPL
34940  * <script type="text/javascript">
34941  */
34942  
34943 // private
34944 // This is a support class used internally by the Grid components
34945 Roo.grid.GridDragZone = function(grid, config){
34946     this.view = grid.getView();
34947     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34948     if(this.view.lockedBody){
34949         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34950         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34951     }
34952     this.scroll = false;
34953     this.grid = grid;
34954     this.ddel = document.createElement('div');
34955     this.ddel.className = 'x-grid-dd-wrap';
34956 };
34957
34958 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34959     ddGroup : "GridDD",
34960
34961     getDragData : function(e){
34962         var t = Roo.lib.Event.getTarget(e);
34963         var rowIndex = this.view.findRowIndex(t);
34964         var sm = this.grid.selModel;
34965             
34966         //Roo.log(rowIndex);
34967         
34968         if (sm.getSelectedCell) {
34969             // cell selection..
34970             if (!sm.getSelectedCell()) {
34971                 return false;
34972             }
34973             if (rowIndex != sm.getSelectedCell()[0]) {
34974                 return false;
34975             }
34976         
34977         }
34978         
34979         if(rowIndex !== false){
34980             
34981             // if editorgrid.. 
34982             
34983             
34984             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34985                
34986             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34987               //  
34988             //}
34989             if (e.hasModifier()){
34990                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
34991             }
34992             
34993             Roo.log("getDragData");
34994             
34995             return {
34996                 grid: this.grid,
34997                 ddel: this.ddel,
34998                 rowIndex: rowIndex,
34999                 selections:sm.getSelections ? sm.getSelections() : (
35000                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35001                 )
35002             };
35003         }
35004         return false;
35005     },
35006
35007     onInitDrag : function(e){
35008         var data = this.dragData;
35009         this.ddel.innerHTML = this.grid.getDragDropText();
35010         this.proxy.update(this.ddel);
35011         // fire start drag?
35012     },
35013
35014     afterRepair : function(){
35015         this.dragging = false;
35016     },
35017
35018     getRepairXY : function(e, data){
35019         return false;
35020     },
35021
35022     onEndDrag : function(data, e){
35023         // fire end drag?
35024     },
35025
35026     onValidDrop : function(dd, e, id){
35027         // fire drag drop?
35028         this.hideProxy();
35029     },
35030
35031     beforeInvalidDrop : function(e, id){
35032
35033     }
35034 });/*
35035  * Based on:
35036  * Ext JS Library 1.1.1
35037  * Copyright(c) 2006-2007, Ext JS, LLC.
35038  *
35039  * Originally Released Under LGPL - original licence link has changed is not relivant.
35040  *
35041  * Fork - LGPL
35042  * <script type="text/javascript">
35043  */
35044  
35045
35046 /**
35047  * @class Roo.grid.ColumnModel
35048  * @extends Roo.util.Observable
35049  * This is the default implementation of a ColumnModel used by the Grid. It defines
35050  * the columns in the grid.
35051  * <br>Usage:<br>
35052  <pre><code>
35053  var colModel = new Roo.grid.ColumnModel([
35054         {header: "Ticker", width: 60, sortable: true, locked: true},
35055         {header: "Company Name", width: 150, sortable: true},
35056         {header: "Market Cap.", width: 100, sortable: true},
35057         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35058         {header: "Employees", width: 100, sortable: true, resizable: false}
35059  ]);
35060  </code></pre>
35061  * <p>
35062  
35063  * The config options listed for this class are options which may appear in each
35064  * individual column definition.
35065  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35066  * @constructor
35067  * @param {Object} config An Array of column config objects. See this class's
35068  * config objects for details.
35069 */
35070 Roo.grid.ColumnModel = function(config){
35071         /**
35072      * The config passed into the constructor
35073      */
35074     this.config = config;
35075     this.lookup = {};
35076
35077     // if no id, create one
35078     // if the column does not have a dataIndex mapping,
35079     // map it to the order it is in the config
35080     for(var i = 0, len = config.length; i < len; i++){
35081         var c = config[i];
35082         if(typeof c.dataIndex == "undefined"){
35083             c.dataIndex = i;
35084         }
35085         if(typeof c.renderer == "string"){
35086             c.renderer = Roo.util.Format[c.renderer];
35087         }
35088         if(typeof c.id == "undefined"){
35089             c.id = Roo.id();
35090         }
35091         if(c.editor && c.editor.xtype){
35092             c.editor  = Roo.factory(c.editor, Roo.grid);
35093         }
35094         if(c.editor && c.editor.isFormField){
35095             c.editor = new Roo.grid.GridEditor(c.editor);
35096         }
35097         this.lookup[c.id] = c;
35098     }
35099
35100     /**
35101      * The width of columns which have no width specified (defaults to 100)
35102      * @type Number
35103      */
35104     this.defaultWidth = 100;
35105
35106     /**
35107      * Default sortable of columns which have no sortable specified (defaults to false)
35108      * @type Boolean
35109      */
35110     this.defaultSortable = false;
35111
35112     this.addEvents({
35113         /**
35114              * @event widthchange
35115              * Fires when the width of a column changes.
35116              * @param {ColumnModel} this
35117              * @param {Number} columnIndex The column index
35118              * @param {Number} newWidth The new width
35119              */
35120             "widthchange": true,
35121         /**
35122              * @event headerchange
35123              * Fires when the text of a header changes.
35124              * @param {ColumnModel} this
35125              * @param {Number} columnIndex The column index
35126              * @param {Number} newText The new header text
35127              */
35128             "headerchange": true,
35129         /**
35130              * @event hiddenchange
35131              * Fires when a column is hidden or "unhidden".
35132              * @param {ColumnModel} this
35133              * @param {Number} columnIndex The column index
35134              * @param {Boolean} hidden true if hidden, false otherwise
35135              */
35136             "hiddenchange": true,
35137             /**
35138          * @event columnmoved
35139          * Fires when a column is moved.
35140          * @param {ColumnModel} this
35141          * @param {Number} oldIndex
35142          * @param {Number} newIndex
35143          */
35144         "columnmoved" : true,
35145         /**
35146          * @event columlockchange
35147          * Fires when a column's locked state is changed
35148          * @param {ColumnModel} this
35149          * @param {Number} colIndex
35150          * @param {Boolean} locked true if locked
35151          */
35152         "columnlockchange" : true
35153     });
35154     Roo.grid.ColumnModel.superclass.constructor.call(this);
35155 };
35156 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35157     /**
35158      * @cfg {String} header The header text to display in the Grid view.
35159      */
35160     /**
35161      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35162      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35163      * specified, the column's index is used as an index into the Record's data Array.
35164      */
35165     /**
35166      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35167      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35168      */
35169     /**
35170      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35171      * Defaults to the value of the {@link #defaultSortable} property.
35172      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35173      */
35174     /**
35175      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35176      */
35177     /**
35178      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35179      */
35180     /**
35181      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35182      */
35183     /**
35184      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35185      */
35186     /**
35187      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35188      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35189      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35190      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35191      */
35192        /**
35193      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35194      */
35195     /**
35196      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35197      */
35198     /**
35199      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35200      */
35201     /**
35202      * @cfg {String} cursor (Optional)
35203      */
35204     /**
35205      * @cfg {String} tooltip (Optional)
35206      */
35207     /**
35208      * @cfg {Number} xs (Optional)
35209      */
35210     /**
35211      * @cfg {Number} sm (Optional)
35212      */
35213     /**
35214      * @cfg {Number} md (Optional)
35215      */
35216     /**
35217      * @cfg {Number} lg (Optional)
35218      */
35219     /**
35220      * Returns the id of the column at the specified index.
35221      * @param {Number} index The column index
35222      * @return {String} the id
35223      */
35224     getColumnId : function(index){
35225         return this.config[index].id;
35226     },
35227
35228     /**
35229      * Returns the column for a specified id.
35230      * @param {String} id The column id
35231      * @return {Object} the column
35232      */
35233     getColumnById : function(id){
35234         return this.lookup[id];
35235     },
35236
35237     
35238     /**
35239      * Returns the column for a specified dataIndex.
35240      * @param {String} dataIndex The column dataIndex
35241      * @return {Object|Boolean} the column or false if not found
35242      */
35243     getColumnByDataIndex: function(dataIndex){
35244         var index = this.findColumnIndex(dataIndex);
35245         return index > -1 ? this.config[index] : false;
35246     },
35247     
35248     /**
35249      * Returns the index for a specified column id.
35250      * @param {String} id The column id
35251      * @return {Number} the index, or -1 if not found
35252      */
35253     getIndexById : function(id){
35254         for(var i = 0, len = this.config.length; i < len; i++){
35255             if(this.config[i].id == id){
35256                 return i;
35257             }
35258         }
35259         return -1;
35260     },
35261     
35262     /**
35263      * Returns the index for a specified column dataIndex.
35264      * @param {String} dataIndex The column dataIndex
35265      * @return {Number} the index, or -1 if not found
35266      */
35267     
35268     findColumnIndex : function(dataIndex){
35269         for(var i = 0, len = this.config.length; i < len; i++){
35270             if(this.config[i].dataIndex == dataIndex){
35271                 return i;
35272             }
35273         }
35274         return -1;
35275     },
35276     
35277     
35278     moveColumn : function(oldIndex, newIndex){
35279         var c = this.config[oldIndex];
35280         this.config.splice(oldIndex, 1);
35281         this.config.splice(newIndex, 0, c);
35282         this.dataMap = null;
35283         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35284     },
35285
35286     isLocked : function(colIndex){
35287         return this.config[colIndex].locked === true;
35288     },
35289
35290     setLocked : function(colIndex, value, suppressEvent){
35291         if(this.isLocked(colIndex) == value){
35292             return;
35293         }
35294         this.config[colIndex].locked = value;
35295         if(!suppressEvent){
35296             this.fireEvent("columnlockchange", this, colIndex, value);
35297         }
35298     },
35299
35300     getTotalLockedWidth : function(){
35301         var totalWidth = 0;
35302         for(var i = 0; i < this.config.length; i++){
35303             if(this.isLocked(i) && !this.isHidden(i)){
35304                 this.totalWidth += this.getColumnWidth(i);
35305             }
35306         }
35307         return totalWidth;
35308     },
35309
35310     getLockedCount : function(){
35311         for(var i = 0, len = this.config.length; i < len; i++){
35312             if(!this.isLocked(i)){
35313                 return i;
35314             }
35315         }
35316         
35317         return this.config.length;
35318     },
35319
35320     /**
35321      * Returns the number of columns.
35322      * @return {Number}
35323      */
35324     getColumnCount : function(visibleOnly){
35325         if(visibleOnly === true){
35326             var c = 0;
35327             for(var i = 0, len = this.config.length; i < len; i++){
35328                 if(!this.isHidden(i)){
35329                     c++;
35330                 }
35331             }
35332             return c;
35333         }
35334         return this.config.length;
35335     },
35336
35337     /**
35338      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35339      * @param {Function} fn
35340      * @param {Object} scope (optional)
35341      * @return {Array} result
35342      */
35343     getColumnsBy : function(fn, scope){
35344         var r = [];
35345         for(var i = 0, len = this.config.length; i < len; i++){
35346             var c = this.config[i];
35347             if(fn.call(scope||this, c, i) === true){
35348                 r[r.length] = c;
35349             }
35350         }
35351         return r;
35352     },
35353
35354     /**
35355      * Returns true if the specified column is sortable.
35356      * @param {Number} col The column index
35357      * @return {Boolean}
35358      */
35359     isSortable : function(col){
35360         if(typeof this.config[col].sortable == "undefined"){
35361             return this.defaultSortable;
35362         }
35363         return this.config[col].sortable;
35364     },
35365
35366     /**
35367      * Returns the rendering (formatting) function defined for the column.
35368      * @param {Number} col The column index.
35369      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35370      */
35371     getRenderer : function(col){
35372         if(!this.config[col].renderer){
35373             return Roo.grid.ColumnModel.defaultRenderer;
35374         }
35375         return this.config[col].renderer;
35376     },
35377
35378     /**
35379      * Sets the rendering (formatting) function for a column.
35380      * @param {Number} col The column index
35381      * @param {Function} fn The function to use to process the cell's raw data
35382      * to return HTML markup for the grid view. The render function is called with
35383      * the following parameters:<ul>
35384      * <li>Data value.</li>
35385      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35386      * <li>css A CSS style string to apply to the table cell.</li>
35387      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35388      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35389      * <li>Row index</li>
35390      * <li>Column index</li>
35391      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35392      */
35393     setRenderer : function(col, fn){
35394         this.config[col].renderer = fn;
35395     },
35396
35397     /**
35398      * Returns the width for the specified column.
35399      * @param {Number} col The column index
35400      * @return {Number}
35401      */
35402     getColumnWidth : function(col){
35403         return this.config[col].width * 1 || this.defaultWidth;
35404     },
35405
35406     /**
35407      * Sets the width for a column.
35408      * @param {Number} col The column index
35409      * @param {Number} width The new width
35410      */
35411     setColumnWidth : function(col, width, suppressEvent){
35412         this.config[col].width = width;
35413         this.totalWidth = null;
35414         if(!suppressEvent){
35415              this.fireEvent("widthchange", this, col, width);
35416         }
35417     },
35418
35419     /**
35420      * Returns the total width of all columns.
35421      * @param {Boolean} includeHidden True to include hidden column widths
35422      * @return {Number}
35423      */
35424     getTotalWidth : function(includeHidden){
35425         if(!this.totalWidth){
35426             this.totalWidth = 0;
35427             for(var i = 0, len = this.config.length; i < len; i++){
35428                 if(includeHidden || !this.isHidden(i)){
35429                     this.totalWidth += this.getColumnWidth(i);
35430                 }
35431             }
35432         }
35433         return this.totalWidth;
35434     },
35435
35436     /**
35437      * Returns the header for the specified column.
35438      * @param {Number} col The column index
35439      * @return {String}
35440      */
35441     getColumnHeader : function(col){
35442         return this.config[col].header;
35443     },
35444
35445     /**
35446      * Sets the header for a column.
35447      * @param {Number} col The column index
35448      * @param {String} header The new header
35449      */
35450     setColumnHeader : function(col, header){
35451         this.config[col].header = header;
35452         this.fireEvent("headerchange", this, col, header);
35453     },
35454
35455     /**
35456      * Returns the tooltip for the specified column.
35457      * @param {Number} col The column index
35458      * @return {String}
35459      */
35460     getColumnTooltip : function(col){
35461             return this.config[col].tooltip;
35462     },
35463     /**
35464      * Sets the tooltip for a column.
35465      * @param {Number} col The column index
35466      * @param {String} tooltip The new tooltip
35467      */
35468     setColumnTooltip : function(col, tooltip){
35469             this.config[col].tooltip = tooltip;
35470     },
35471
35472     /**
35473      * Returns the dataIndex for the specified column.
35474      * @param {Number} col The column index
35475      * @return {Number}
35476      */
35477     getDataIndex : function(col){
35478         return this.config[col].dataIndex;
35479     },
35480
35481     /**
35482      * Sets the dataIndex for a column.
35483      * @param {Number} col The column index
35484      * @param {Number} dataIndex The new dataIndex
35485      */
35486     setDataIndex : function(col, dataIndex){
35487         this.config[col].dataIndex = dataIndex;
35488     },
35489
35490     
35491     
35492     /**
35493      * Returns true if the cell is editable.
35494      * @param {Number} colIndex The column index
35495      * @param {Number} rowIndex The row index - this is nto actually used..?
35496      * @return {Boolean}
35497      */
35498     isCellEditable : function(colIndex, rowIndex){
35499         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35500     },
35501
35502     /**
35503      * Returns the editor defined for the cell/column.
35504      * return false or null to disable editing.
35505      * @param {Number} colIndex The column index
35506      * @param {Number} rowIndex The row index
35507      * @return {Object}
35508      */
35509     getCellEditor : function(colIndex, rowIndex){
35510         return this.config[colIndex].editor;
35511     },
35512
35513     /**
35514      * Sets if a column is editable.
35515      * @param {Number} col The column index
35516      * @param {Boolean} editable True if the column is editable
35517      */
35518     setEditable : function(col, editable){
35519         this.config[col].editable = editable;
35520     },
35521
35522
35523     /**
35524      * Returns true if the column is hidden.
35525      * @param {Number} colIndex The column index
35526      * @return {Boolean}
35527      */
35528     isHidden : function(colIndex){
35529         return this.config[colIndex].hidden;
35530     },
35531
35532
35533     /**
35534      * Returns true if the column width cannot be changed
35535      */
35536     isFixed : function(colIndex){
35537         return this.config[colIndex].fixed;
35538     },
35539
35540     /**
35541      * Returns true if the column can be resized
35542      * @return {Boolean}
35543      */
35544     isResizable : function(colIndex){
35545         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35546     },
35547     /**
35548      * Sets if a column is hidden.
35549      * @param {Number} colIndex The column index
35550      * @param {Boolean} hidden True if the column is hidden
35551      */
35552     setHidden : function(colIndex, hidden){
35553         this.config[colIndex].hidden = hidden;
35554         this.totalWidth = null;
35555         this.fireEvent("hiddenchange", this, colIndex, hidden);
35556     },
35557
35558     /**
35559      * Sets the editor for a column.
35560      * @param {Number} col The column index
35561      * @param {Object} editor The editor object
35562      */
35563     setEditor : function(col, editor){
35564         this.config[col].editor = editor;
35565     }
35566 });
35567
35568 Roo.grid.ColumnModel.defaultRenderer = function(value)
35569 {
35570     if(typeof value == "object") {
35571         return value;
35572     }
35573         if(typeof value == "string" && value.length < 1){
35574             return "&#160;";
35575         }
35576     
35577         return String.format("{0}", value);
35578 };
35579
35580 // Alias for backwards compatibility
35581 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35582 /*
35583  * Based on:
35584  * Ext JS Library 1.1.1
35585  * Copyright(c) 2006-2007, Ext JS, LLC.
35586  *
35587  * Originally Released Under LGPL - original licence link has changed is not relivant.
35588  *
35589  * Fork - LGPL
35590  * <script type="text/javascript">
35591  */
35592
35593 /**
35594  * @class Roo.grid.AbstractSelectionModel
35595  * @extends Roo.util.Observable
35596  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35597  * implemented by descendant classes.  This class should not be directly instantiated.
35598  * @constructor
35599  */
35600 Roo.grid.AbstractSelectionModel = function(){
35601     this.locked = false;
35602     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35603 };
35604
35605 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35606     /** @ignore Called by the grid automatically. Do not call directly. */
35607     init : function(grid){
35608         this.grid = grid;
35609         this.initEvents();
35610     },
35611
35612     /**
35613      * Locks the selections.
35614      */
35615     lock : function(){
35616         this.locked = true;
35617     },
35618
35619     /**
35620      * Unlocks the selections.
35621      */
35622     unlock : function(){
35623         this.locked = false;
35624     },
35625
35626     /**
35627      * Returns true if the selections are locked.
35628      * @return {Boolean}
35629      */
35630     isLocked : function(){
35631         return this.locked;
35632     }
35633 });/*
35634  * Based on:
35635  * Ext JS Library 1.1.1
35636  * Copyright(c) 2006-2007, Ext JS, LLC.
35637  *
35638  * Originally Released Under LGPL - original licence link has changed is not relivant.
35639  *
35640  * Fork - LGPL
35641  * <script type="text/javascript">
35642  */
35643 /**
35644  * @extends Roo.grid.AbstractSelectionModel
35645  * @class Roo.grid.RowSelectionModel
35646  * The default SelectionModel used by {@link Roo.grid.Grid}.
35647  * It supports multiple selections and keyboard selection/navigation. 
35648  * @constructor
35649  * @param {Object} config
35650  */
35651 Roo.grid.RowSelectionModel = function(config){
35652     Roo.apply(this, config);
35653     this.selections = new Roo.util.MixedCollection(false, function(o){
35654         return o.id;
35655     });
35656
35657     this.last = false;
35658     this.lastActive = false;
35659
35660     this.addEvents({
35661         /**
35662              * @event selectionchange
35663              * Fires when the selection changes
35664              * @param {SelectionModel} this
35665              */
35666             "selectionchange" : true,
35667         /**
35668              * @event afterselectionchange
35669              * Fires after the selection changes (eg. by key press or clicking)
35670              * @param {SelectionModel} this
35671              */
35672             "afterselectionchange" : true,
35673         /**
35674              * @event beforerowselect
35675              * Fires when a row is selected being selected, return false to cancel.
35676              * @param {SelectionModel} this
35677              * @param {Number} rowIndex The selected index
35678              * @param {Boolean} keepExisting False if other selections will be cleared
35679              */
35680             "beforerowselect" : true,
35681         /**
35682              * @event rowselect
35683              * Fires when a row is selected.
35684              * @param {SelectionModel} this
35685              * @param {Number} rowIndex The selected index
35686              * @param {Roo.data.Record} r The record
35687              */
35688             "rowselect" : true,
35689         /**
35690              * @event rowdeselect
35691              * Fires when a row is deselected.
35692              * @param {SelectionModel} this
35693              * @param {Number} rowIndex The selected index
35694              */
35695         "rowdeselect" : true
35696     });
35697     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35698     this.locked = false;
35699 };
35700
35701 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35702     /**
35703      * @cfg {Boolean} singleSelect
35704      * True to allow selection of only one row at a time (defaults to false)
35705      */
35706     singleSelect : false,
35707
35708     // private
35709     initEvents : function(){
35710
35711         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35712             this.grid.on("mousedown", this.handleMouseDown, this);
35713         }else{ // allow click to work like normal
35714             this.grid.on("rowclick", this.handleDragableRowClick, this);
35715         }
35716
35717         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35718             "up" : function(e){
35719                 if(!e.shiftKey){
35720                     this.selectPrevious(e.shiftKey);
35721                 }else if(this.last !== false && this.lastActive !== false){
35722                     var last = this.last;
35723                     this.selectRange(this.last,  this.lastActive-1);
35724                     this.grid.getView().focusRow(this.lastActive);
35725                     if(last !== false){
35726                         this.last = last;
35727                     }
35728                 }else{
35729                     this.selectFirstRow();
35730                 }
35731                 this.fireEvent("afterselectionchange", this);
35732             },
35733             "down" : function(e){
35734                 if(!e.shiftKey){
35735                     this.selectNext(e.shiftKey);
35736                 }else if(this.last !== false && this.lastActive !== false){
35737                     var last = this.last;
35738                     this.selectRange(this.last,  this.lastActive+1);
35739                     this.grid.getView().focusRow(this.lastActive);
35740                     if(last !== false){
35741                         this.last = last;
35742                     }
35743                 }else{
35744                     this.selectFirstRow();
35745                 }
35746                 this.fireEvent("afterselectionchange", this);
35747             },
35748             scope: this
35749         });
35750
35751         var view = this.grid.view;
35752         view.on("refresh", this.onRefresh, this);
35753         view.on("rowupdated", this.onRowUpdated, this);
35754         view.on("rowremoved", this.onRemove, this);
35755     },
35756
35757     // private
35758     onRefresh : function(){
35759         var ds = this.grid.dataSource, i, v = this.grid.view;
35760         var s = this.selections;
35761         s.each(function(r){
35762             if((i = ds.indexOfId(r.id)) != -1){
35763                 v.onRowSelect(i);
35764                 s.add(ds.getAt(i)); // updating the selection relate data
35765             }else{
35766                 s.remove(r);
35767             }
35768         });
35769     },
35770
35771     // private
35772     onRemove : function(v, index, r){
35773         this.selections.remove(r);
35774     },
35775
35776     // private
35777     onRowUpdated : function(v, index, r){
35778         if(this.isSelected(r)){
35779             v.onRowSelect(index);
35780         }
35781     },
35782
35783     /**
35784      * Select records.
35785      * @param {Array} records The records to select
35786      * @param {Boolean} keepExisting (optional) True to keep existing selections
35787      */
35788     selectRecords : function(records, keepExisting){
35789         if(!keepExisting){
35790             this.clearSelections();
35791         }
35792         var ds = this.grid.dataSource;
35793         for(var i = 0, len = records.length; i < len; i++){
35794             this.selectRow(ds.indexOf(records[i]), true);
35795         }
35796     },
35797
35798     /**
35799      * Gets the number of selected rows.
35800      * @return {Number}
35801      */
35802     getCount : function(){
35803         return this.selections.length;
35804     },
35805
35806     /**
35807      * Selects the first row in the grid.
35808      */
35809     selectFirstRow : function(){
35810         this.selectRow(0);
35811     },
35812
35813     /**
35814      * Select the last row.
35815      * @param {Boolean} keepExisting (optional) True to keep existing selections
35816      */
35817     selectLastRow : function(keepExisting){
35818         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35819     },
35820
35821     /**
35822      * Selects the row immediately following the last selected row.
35823      * @param {Boolean} keepExisting (optional) True to keep existing selections
35824      */
35825     selectNext : function(keepExisting){
35826         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35827             this.selectRow(this.last+1, keepExisting);
35828             this.grid.getView().focusRow(this.last);
35829         }
35830     },
35831
35832     /**
35833      * Selects the row that precedes the last selected row.
35834      * @param {Boolean} keepExisting (optional) True to keep existing selections
35835      */
35836     selectPrevious : function(keepExisting){
35837         if(this.last){
35838             this.selectRow(this.last-1, keepExisting);
35839             this.grid.getView().focusRow(this.last);
35840         }
35841     },
35842
35843     /**
35844      * Returns the selected records
35845      * @return {Array} Array of selected records
35846      */
35847     getSelections : function(){
35848         return [].concat(this.selections.items);
35849     },
35850
35851     /**
35852      * Returns the first selected record.
35853      * @return {Record}
35854      */
35855     getSelected : function(){
35856         return this.selections.itemAt(0);
35857     },
35858
35859
35860     /**
35861      * Clears all selections.
35862      */
35863     clearSelections : function(fast){
35864         if(this.locked) {
35865             return;
35866         }
35867         if(fast !== true){
35868             var ds = this.grid.dataSource;
35869             var s = this.selections;
35870             s.each(function(r){
35871                 this.deselectRow(ds.indexOfId(r.id));
35872             }, this);
35873             s.clear();
35874         }else{
35875             this.selections.clear();
35876         }
35877         this.last = false;
35878     },
35879
35880
35881     /**
35882      * Selects all rows.
35883      */
35884     selectAll : function(){
35885         if(this.locked) {
35886             return;
35887         }
35888         this.selections.clear();
35889         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35890             this.selectRow(i, true);
35891         }
35892     },
35893
35894     /**
35895      * Returns True if there is a selection.
35896      * @return {Boolean}
35897      */
35898     hasSelection : function(){
35899         return this.selections.length > 0;
35900     },
35901
35902     /**
35903      * Returns True if the specified row is selected.
35904      * @param {Number/Record} record The record or index of the record to check
35905      * @return {Boolean}
35906      */
35907     isSelected : function(index){
35908         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35909         return (r && this.selections.key(r.id) ? true : false);
35910     },
35911
35912     /**
35913      * Returns True if the specified record id is selected.
35914      * @param {String} id The id of record to check
35915      * @return {Boolean}
35916      */
35917     isIdSelected : function(id){
35918         return (this.selections.key(id) ? true : false);
35919     },
35920
35921     // private
35922     handleMouseDown : function(e, t){
35923         var view = this.grid.getView(), rowIndex;
35924         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35925             return;
35926         };
35927         if(e.shiftKey && this.last !== false){
35928             var last = this.last;
35929             this.selectRange(last, rowIndex, e.ctrlKey);
35930             this.last = last; // reset the last
35931             view.focusRow(rowIndex);
35932         }else{
35933             var isSelected = this.isSelected(rowIndex);
35934             if(e.button !== 0 && isSelected){
35935                 view.focusRow(rowIndex);
35936             }else if(e.ctrlKey && isSelected){
35937                 this.deselectRow(rowIndex);
35938             }else if(!isSelected){
35939                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35940                 view.focusRow(rowIndex);
35941             }
35942         }
35943         this.fireEvent("afterselectionchange", this);
35944     },
35945     // private
35946     handleDragableRowClick :  function(grid, rowIndex, e) 
35947     {
35948         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35949             this.selectRow(rowIndex, false);
35950             grid.view.focusRow(rowIndex);
35951              this.fireEvent("afterselectionchange", this);
35952         }
35953     },
35954     
35955     /**
35956      * Selects multiple rows.
35957      * @param {Array} rows Array of the indexes of the row to select
35958      * @param {Boolean} keepExisting (optional) True to keep existing selections
35959      */
35960     selectRows : function(rows, keepExisting){
35961         if(!keepExisting){
35962             this.clearSelections();
35963         }
35964         for(var i = 0, len = rows.length; i < len; i++){
35965             this.selectRow(rows[i], true);
35966         }
35967     },
35968
35969     /**
35970      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35971      * @param {Number} startRow The index of the first row in the range
35972      * @param {Number} endRow The index of the last row in the range
35973      * @param {Boolean} keepExisting (optional) True to retain existing selections
35974      */
35975     selectRange : function(startRow, endRow, keepExisting){
35976         if(this.locked) {
35977             return;
35978         }
35979         if(!keepExisting){
35980             this.clearSelections();
35981         }
35982         if(startRow <= endRow){
35983             for(var i = startRow; i <= endRow; i++){
35984                 this.selectRow(i, true);
35985             }
35986         }else{
35987             for(var i = startRow; i >= endRow; i--){
35988                 this.selectRow(i, true);
35989             }
35990         }
35991     },
35992
35993     /**
35994      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
35995      * @param {Number} startRow The index of the first row in the range
35996      * @param {Number} endRow The index of the last row in the range
35997      */
35998     deselectRange : function(startRow, endRow, preventViewNotify){
35999         if(this.locked) {
36000             return;
36001         }
36002         for(var i = startRow; i <= endRow; i++){
36003             this.deselectRow(i, preventViewNotify);
36004         }
36005     },
36006
36007     /**
36008      * Selects a row.
36009      * @param {Number} row The index of the row to select
36010      * @param {Boolean} keepExisting (optional) True to keep existing selections
36011      */
36012     selectRow : function(index, keepExisting, preventViewNotify){
36013         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36014             return;
36015         }
36016         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36017             if(!keepExisting || this.singleSelect){
36018                 this.clearSelections();
36019             }
36020             var r = this.grid.dataSource.getAt(index);
36021             this.selections.add(r);
36022             this.last = this.lastActive = index;
36023             if(!preventViewNotify){
36024                 this.grid.getView().onRowSelect(index);
36025             }
36026             this.fireEvent("rowselect", this, index, r);
36027             this.fireEvent("selectionchange", this);
36028         }
36029     },
36030
36031     /**
36032      * Deselects a row.
36033      * @param {Number} row The index of the row to deselect
36034      */
36035     deselectRow : function(index, preventViewNotify){
36036         if(this.locked) {
36037             return;
36038         }
36039         if(this.last == index){
36040             this.last = false;
36041         }
36042         if(this.lastActive == index){
36043             this.lastActive = false;
36044         }
36045         var r = this.grid.dataSource.getAt(index);
36046         this.selections.remove(r);
36047         if(!preventViewNotify){
36048             this.grid.getView().onRowDeselect(index);
36049         }
36050         this.fireEvent("rowdeselect", this, index);
36051         this.fireEvent("selectionchange", this);
36052     },
36053
36054     // private
36055     restoreLast : function(){
36056         if(this._last){
36057             this.last = this._last;
36058         }
36059     },
36060
36061     // private
36062     acceptsNav : function(row, col, cm){
36063         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36064     },
36065
36066     // private
36067     onEditorKey : function(field, e){
36068         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36069         if(k == e.TAB){
36070             e.stopEvent();
36071             ed.completeEdit();
36072             if(e.shiftKey){
36073                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36074             }else{
36075                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36076             }
36077         }else if(k == e.ENTER && !e.ctrlKey){
36078             e.stopEvent();
36079             ed.completeEdit();
36080             if(e.shiftKey){
36081                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36082             }else{
36083                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36084             }
36085         }else if(k == e.ESC){
36086             ed.cancelEdit();
36087         }
36088         if(newCell){
36089             g.startEditing(newCell[0], newCell[1]);
36090         }
36091     }
36092 });/*
36093  * Based on:
36094  * Ext JS Library 1.1.1
36095  * Copyright(c) 2006-2007, Ext JS, LLC.
36096  *
36097  * Originally Released Under LGPL - original licence link has changed is not relivant.
36098  *
36099  * Fork - LGPL
36100  * <script type="text/javascript">
36101  */
36102 /**
36103  * @class Roo.grid.CellSelectionModel
36104  * @extends Roo.grid.AbstractSelectionModel
36105  * This class provides the basic implementation for cell selection in a grid.
36106  * @constructor
36107  * @param {Object} config The object containing the configuration of this model.
36108  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36109  */
36110 Roo.grid.CellSelectionModel = function(config){
36111     Roo.apply(this, config);
36112
36113     this.selection = null;
36114
36115     this.addEvents({
36116         /**
36117              * @event beforerowselect
36118              * Fires before a cell is selected.
36119              * @param {SelectionModel} this
36120              * @param {Number} rowIndex The selected row index
36121              * @param {Number} colIndex The selected cell index
36122              */
36123             "beforecellselect" : true,
36124         /**
36125              * @event cellselect
36126              * Fires when a cell is selected.
36127              * @param {SelectionModel} this
36128              * @param {Number} rowIndex The selected row index
36129              * @param {Number} colIndex The selected cell index
36130              */
36131             "cellselect" : true,
36132         /**
36133              * @event selectionchange
36134              * Fires when the active selection changes.
36135              * @param {SelectionModel} this
36136              * @param {Object} selection null for no selection or an object (o) with two properties
36137                 <ul>
36138                 <li>o.record: the record object for the row the selection is in</li>
36139                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36140                 </ul>
36141              */
36142             "selectionchange" : true,
36143         /**
36144              * @event tabend
36145              * Fires when the tab (or enter) was pressed on the last editable cell
36146              * You can use this to trigger add new row.
36147              * @param {SelectionModel} this
36148              */
36149             "tabend" : true,
36150          /**
36151              * @event beforeeditnext
36152              * Fires before the next editable sell is made active
36153              * You can use this to skip to another cell or fire the tabend
36154              *    if you set cell to false
36155              * @param {Object} eventdata object : { cell : [ row, col ] } 
36156              */
36157             "beforeeditnext" : true
36158     });
36159     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36160 };
36161
36162 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36163     
36164     enter_is_tab: false,
36165
36166     /** @ignore */
36167     initEvents : function(){
36168         this.grid.on("mousedown", this.handleMouseDown, this);
36169         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36170         var view = this.grid.view;
36171         view.on("refresh", this.onViewChange, this);
36172         view.on("rowupdated", this.onRowUpdated, this);
36173         view.on("beforerowremoved", this.clearSelections, this);
36174         view.on("beforerowsinserted", this.clearSelections, this);
36175         if(this.grid.isEditor){
36176             this.grid.on("beforeedit", this.beforeEdit,  this);
36177         }
36178     },
36179
36180         //private
36181     beforeEdit : function(e){
36182         this.select(e.row, e.column, false, true, e.record);
36183     },
36184
36185         //private
36186     onRowUpdated : function(v, index, r){
36187         if(this.selection && this.selection.record == r){
36188             v.onCellSelect(index, this.selection.cell[1]);
36189         }
36190     },
36191
36192         //private
36193     onViewChange : function(){
36194         this.clearSelections(true);
36195     },
36196
36197         /**
36198          * Returns the currently selected cell,.
36199          * @return {Array} The selected cell (row, column) or null if none selected.
36200          */
36201     getSelectedCell : function(){
36202         return this.selection ? this.selection.cell : null;
36203     },
36204
36205     /**
36206      * Clears all selections.
36207      * @param {Boolean} true to prevent the gridview from being notified about the change.
36208      */
36209     clearSelections : function(preventNotify){
36210         var s = this.selection;
36211         if(s){
36212             if(preventNotify !== true){
36213                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36214             }
36215             this.selection = null;
36216             this.fireEvent("selectionchange", this, null);
36217         }
36218     },
36219
36220     /**
36221      * Returns true if there is a selection.
36222      * @return {Boolean}
36223      */
36224     hasSelection : function(){
36225         return this.selection ? true : false;
36226     },
36227
36228     /** @ignore */
36229     handleMouseDown : function(e, t){
36230         var v = this.grid.getView();
36231         if(this.isLocked()){
36232             return;
36233         };
36234         var row = v.findRowIndex(t);
36235         var cell = v.findCellIndex(t);
36236         if(row !== false && cell !== false){
36237             this.select(row, cell);
36238         }
36239     },
36240
36241     /**
36242      * Selects a cell.
36243      * @param {Number} rowIndex
36244      * @param {Number} collIndex
36245      */
36246     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36247         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36248             this.clearSelections();
36249             r = r || this.grid.dataSource.getAt(rowIndex);
36250             this.selection = {
36251                 record : r,
36252                 cell : [rowIndex, colIndex]
36253             };
36254             if(!preventViewNotify){
36255                 var v = this.grid.getView();
36256                 v.onCellSelect(rowIndex, colIndex);
36257                 if(preventFocus !== true){
36258                     v.focusCell(rowIndex, colIndex);
36259                 }
36260             }
36261             this.fireEvent("cellselect", this, rowIndex, colIndex);
36262             this.fireEvent("selectionchange", this, this.selection);
36263         }
36264     },
36265
36266         //private
36267     isSelectable : function(rowIndex, colIndex, cm){
36268         return !cm.isHidden(colIndex);
36269     },
36270
36271     /** @ignore */
36272     handleKeyDown : function(e){
36273         //Roo.log('Cell Sel Model handleKeyDown');
36274         if(!e.isNavKeyPress()){
36275             return;
36276         }
36277         var g = this.grid, s = this.selection;
36278         if(!s){
36279             e.stopEvent();
36280             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36281             if(cell){
36282                 this.select(cell[0], cell[1]);
36283             }
36284             return;
36285         }
36286         var sm = this;
36287         var walk = function(row, col, step){
36288             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36289         };
36290         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36291         var newCell;
36292
36293       
36294
36295         switch(k){
36296             case e.TAB:
36297                 // handled by onEditorKey
36298                 if (g.isEditor && g.editing) {
36299                     return;
36300                 }
36301                 if(e.shiftKey) {
36302                     newCell = walk(r, c-1, -1);
36303                 } else {
36304                     newCell = walk(r, c+1, 1);
36305                 }
36306                 break;
36307             
36308             case e.DOWN:
36309                newCell = walk(r+1, c, 1);
36310                 break;
36311             
36312             case e.UP:
36313                 newCell = walk(r-1, c, -1);
36314                 break;
36315             
36316             case e.RIGHT:
36317                 newCell = walk(r, c+1, 1);
36318                 break;
36319             
36320             case e.LEFT:
36321                 newCell = walk(r, c-1, -1);
36322                 break;
36323             
36324             case e.ENTER:
36325                 
36326                 if(g.isEditor && !g.editing){
36327                    g.startEditing(r, c);
36328                    e.stopEvent();
36329                    return;
36330                 }
36331                 
36332                 
36333              break;
36334         };
36335         if(newCell){
36336             this.select(newCell[0], newCell[1]);
36337             e.stopEvent();
36338             
36339         }
36340     },
36341
36342     acceptsNav : function(row, col, cm){
36343         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36344     },
36345     /**
36346      * Selects a cell.
36347      * @param {Number} field (not used) - as it's normally used as a listener
36348      * @param {Number} e - event - fake it by using
36349      *
36350      * var e = Roo.EventObjectImpl.prototype;
36351      * e.keyCode = e.TAB
36352      *
36353      * 
36354      */
36355     onEditorKey : function(field, e){
36356         
36357         var k = e.getKey(),
36358             newCell,
36359             g = this.grid,
36360             ed = g.activeEditor,
36361             forward = false;
36362         ///Roo.log('onEditorKey' + k);
36363         
36364         
36365         if (this.enter_is_tab && k == e.ENTER) {
36366             k = e.TAB;
36367         }
36368         
36369         if(k == e.TAB){
36370             if(e.shiftKey){
36371                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36372             }else{
36373                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36374                 forward = true;
36375             }
36376             
36377             e.stopEvent();
36378             
36379         } else if(k == e.ENTER &&  !e.ctrlKey){
36380             ed.completeEdit();
36381             e.stopEvent();
36382             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36383         
36384                 } else if(k == e.ESC){
36385             ed.cancelEdit();
36386         }
36387                 
36388         if (newCell) {
36389             var ecall = { cell : newCell, forward : forward };
36390             this.fireEvent('beforeeditnext', ecall );
36391             newCell = ecall.cell;
36392                         forward = ecall.forward;
36393         }
36394                 
36395         if(newCell){
36396             //Roo.log('next cell after edit');
36397             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36398         } else if (forward) {
36399             // tabbed past last
36400             this.fireEvent.defer(100, this, ['tabend',this]);
36401         }
36402     }
36403 });/*
36404  * Based on:
36405  * Ext JS Library 1.1.1
36406  * Copyright(c) 2006-2007, Ext JS, LLC.
36407  *
36408  * Originally Released Under LGPL - original licence link has changed is not relivant.
36409  *
36410  * Fork - LGPL
36411  * <script type="text/javascript">
36412  */
36413  
36414 /**
36415  * @class Roo.grid.EditorGrid
36416  * @extends Roo.grid.Grid
36417  * Class for creating and editable grid.
36418  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36419  * The container MUST have some type of size defined for the grid to fill. The container will be 
36420  * automatically set to position relative if it isn't already.
36421  * @param {Object} dataSource The data model to bind to
36422  * @param {Object} colModel The column model with info about this grid's columns
36423  */
36424 Roo.grid.EditorGrid = function(container, config){
36425     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36426     this.getGridEl().addClass("xedit-grid");
36427
36428     if(!this.selModel){
36429         this.selModel = new Roo.grid.CellSelectionModel();
36430     }
36431
36432     this.activeEditor = null;
36433
36434         this.addEvents({
36435             /**
36436              * @event beforeedit
36437              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36438              * <ul style="padding:5px;padding-left:16px;">
36439              * <li>grid - This grid</li>
36440              * <li>record - The record being edited</li>
36441              * <li>field - The field name being edited</li>
36442              * <li>value - The value for the field being edited.</li>
36443              * <li>row - The grid row index</li>
36444              * <li>column - The grid column index</li>
36445              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36446              * </ul>
36447              * @param {Object} e An edit event (see above for description)
36448              */
36449             "beforeedit" : true,
36450             /**
36451              * @event afteredit
36452              * Fires after a cell is edited. <br />
36453              * <ul style="padding:5px;padding-left:16px;">
36454              * <li>grid - This grid</li>
36455              * <li>record - The record being edited</li>
36456              * <li>field - The field name being edited</li>
36457              * <li>value - The value being set</li>
36458              * <li>originalValue - The original value for the field, before the edit.</li>
36459              * <li>row - The grid row index</li>
36460              * <li>column - The grid column index</li>
36461              * </ul>
36462              * @param {Object} e An edit event (see above for description)
36463              */
36464             "afteredit" : true,
36465             /**
36466              * @event validateedit
36467              * Fires after a cell is edited, but before the value is set in the record. 
36468          * You can use this to modify the value being set in the field, Return false
36469              * to cancel the change. The edit event object has the following properties <br />
36470              * <ul style="padding:5px;padding-left:16px;">
36471          * <li>editor - This editor</li>
36472              * <li>grid - This grid</li>
36473              * <li>record - The record being edited</li>
36474              * <li>field - The field name being edited</li>
36475              * <li>value - The value being set</li>
36476              * <li>originalValue - The original value for the field, before the edit.</li>
36477              * <li>row - The grid row index</li>
36478              * <li>column - The grid column index</li>
36479              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36480              * </ul>
36481              * @param {Object} e An edit event (see above for description)
36482              */
36483             "validateedit" : true
36484         });
36485     this.on("bodyscroll", this.stopEditing,  this);
36486     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36487 };
36488
36489 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36490     /**
36491      * @cfg {Number} clicksToEdit
36492      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36493      */
36494     clicksToEdit: 2,
36495
36496     // private
36497     isEditor : true,
36498     // private
36499     trackMouseOver: false, // causes very odd FF errors
36500
36501     onCellDblClick : function(g, row, col){
36502         this.startEditing(row, col);
36503     },
36504
36505     onEditComplete : function(ed, value, startValue){
36506         this.editing = false;
36507         this.activeEditor = null;
36508         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36509         var r = ed.record;
36510         var field = this.colModel.getDataIndex(ed.col);
36511         var e = {
36512             grid: this,
36513             record: r,
36514             field: field,
36515             originalValue: startValue,
36516             value: value,
36517             row: ed.row,
36518             column: ed.col,
36519             cancel:false,
36520             editor: ed
36521         };
36522         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36523         cell.show();
36524           
36525         if(String(value) !== String(startValue)){
36526             
36527             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36528                 r.set(field, e.value);
36529                 // if we are dealing with a combo box..
36530                 // then we also set the 'name' colum to be the displayField
36531                 if (ed.field.displayField && ed.field.name) {
36532                     r.set(ed.field.name, ed.field.el.dom.value);
36533                 }
36534                 
36535                 delete e.cancel; //?? why!!!
36536                 this.fireEvent("afteredit", e);
36537             }
36538         } else {
36539             this.fireEvent("afteredit", e); // always fire it!
36540         }
36541         this.view.focusCell(ed.row, ed.col);
36542     },
36543
36544     /**
36545      * Starts editing the specified for the specified row/column
36546      * @param {Number} rowIndex
36547      * @param {Number} colIndex
36548      */
36549     startEditing : function(row, col){
36550         this.stopEditing();
36551         if(this.colModel.isCellEditable(col, row)){
36552             this.view.ensureVisible(row, col, true);
36553           
36554             var r = this.dataSource.getAt(row);
36555             var field = this.colModel.getDataIndex(col);
36556             var cell = Roo.get(this.view.getCell(row,col));
36557             var e = {
36558                 grid: this,
36559                 record: r,
36560                 field: field,
36561                 value: r.data[field],
36562                 row: row,
36563                 column: col,
36564                 cancel:false 
36565             };
36566             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36567                 this.editing = true;
36568                 var ed = this.colModel.getCellEditor(col, row);
36569                 
36570                 if (!ed) {
36571                     return;
36572                 }
36573                 if(!ed.rendered){
36574                     ed.render(ed.parentEl || document.body);
36575                 }
36576                 ed.field.reset();
36577                
36578                 cell.hide();
36579                 
36580                 (function(){ // complex but required for focus issues in safari, ie and opera
36581                     ed.row = row;
36582                     ed.col = col;
36583                     ed.record = r;
36584                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36585                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36586                     this.activeEditor = ed;
36587                     var v = r.data[field];
36588                     ed.startEdit(this.view.getCell(row, col), v);
36589                     // combo's with 'displayField and name set
36590                     if (ed.field.displayField && ed.field.name) {
36591                         ed.field.el.dom.value = r.data[ed.field.name];
36592                     }
36593                     
36594                     
36595                 }).defer(50, this);
36596             }
36597         }
36598     },
36599         
36600     /**
36601      * Stops any active editing
36602      */
36603     stopEditing : function(){
36604         if(this.activeEditor){
36605             this.activeEditor.completeEdit();
36606         }
36607         this.activeEditor = null;
36608     },
36609         
36610          /**
36611      * Called to get grid's drag proxy text, by default returns this.ddText.
36612      * @return {String}
36613      */
36614     getDragDropText : function(){
36615         var count = this.selModel.getSelectedCell() ? 1 : 0;
36616         return String.format(this.ddText, count, count == 1 ? '' : 's');
36617     }
36618         
36619 });/*
36620  * Based on:
36621  * Ext JS Library 1.1.1
36622  * Copyright(c) 2006-2007, Ext JS, LLC.
36623  *
36624  * Originally Released Under LGPL - original licence link has changed is not relivant.
36625  *
36626  * Fork - LGPL
36627  * <script type="text/javascript">
36628  */
36629
36630 // private - not really -- you end up using it !
36631 // This is a support class used internally by the Grid components
36632
36633 /**
36634  * @class Roo.grid.GridEditor
36635  * @extends Roo.Editor
36636  * Class for creating and editable grid elements.
36637  * @param {Object} config any settings (must include field)
36638  */
36639 Roo.grid.GridEditor = function(field, config){
36640     if (!config && field.field) {
36641         config = field;
36642         field = Roo.factory(config.field, Roo.form);
36643     }
36644     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36645     field.monitorTab = false;
36646 };
36647
36648 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36649     
36650     /**
36651      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36652      */
36653     
36654     alignment: "tl-tl",
36655     autoSize: "width",
36656     hideEl : false,
36657     cls: "x-small-editor x-grid-editor",
36658     shim:false,
36659     shadow:"frame"
36660 });/*
36661  * Based on:
36662  * Ext JS Library 1.1.1
36663  * Copyright(c) 2006-2007, Ext JS, LLC.
36664  *
36665  * Originally Released Under LGPL - original licence link has changed is not relivant.
36666  *
36667  * Fork - LGPL
36668  * <script type="text/javascript">
36669  */
36670   
36671
36672   
36673 Roo.grid.PropertyRecord = Roo.data.Record.create([
36674     {name:'name',type:'string'},  'value'
36675 ]);
36676
36677
36678 Roo.grid.PropertyStore = function(grid, source){
36679     this.grid = grid;
36680     this.store = new Roo.data.Store({
36681         recordType : Roo.grid.PropertyRecord
36682     });
36683     this.store.on('update', this.onUpdate,  this);
36684     if(source){
36685         this.setSource(source);
36686     }
36687     Roo.grid.PropertyStore.superclass.constructor.call(this);
36688 };
36689
36690
36691
36692 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36693     setSource : function(o){
36694         this.source = o;
36695         this.store.removeAll();
36696         var data = [];
36697         for(var k in o){
36698             if(this.isEditableValue(o[k])){
36699                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36700             }
36701         }
36702         this.store.loadRecords({records: data}, {}, true);
36703     },
36704
36705     onUpdate : function(ds, record, type){
36706         if(type == Roo.data.Record.EDIT){
36707             var v = record.data['value'];
36708             var oldValue = record.modified['value'];
36709             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36710                 this.source[record.id] = v;
36711                 record.commit();
36712                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36713             }else{
36714                 record.reject();
36715             }
36716         }
36717     },
36718
36719     getProperty : function(row){
36720        return this.store.getAt(row);
36721     },
36722
36723     isEditableValue: function(val){
36724         if(val && val instanceof Date){
36725             return true;
36726         }else if(typeof val == 'object' || typeof val == 'function'){
36727             return false;
36728         }
36729         return true;
36730     },
36731
36732     setValue : function(prop, value){
36733         this.source[prop] = value;
36734         this.store.getById(prop).set('value', value);
36735     },
36736
36737     getSource : function(){
36738         return this.source;
36739     }
36740 });
36741
36742 Roo.grid.PropertyColumnModel = function(grid, store){
36743     this.grid = grid;
36744     var g = Roo.grid;
36745     g.PropertyColumnModel.superclass.constructor.call(this, [
36746         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36747         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36748     ]);
36749     this.store = store;
36750     this.bselect = Roo.DomHelper.append(document.body, {
36751         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36752             {tag: 'option', value: 'true', html: 'true'},
36753             {tag: 'option', value: 'false', html: 'false'}
36754         ]
36755     });
36756     Roo.id(this.bselect);
36757     var f = Roo.form;
36758     this.editors = {
36759         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36760         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36761         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36762         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36763         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36764     };
36765     this.renderCellDelegate = this.renderCell.createDelegate(this);
36766     this.renderPropDelegate = this.renderProp.createDelegate(this);
36767 };
36768
36769 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36770     
36771     
36772     nameText : 'Name',
36773     valueText : 'Value',
36774     
36775     dateFormat : 'm/j/Y',
36776     
36777     
36778     renderDate : function(dateVal){
36779         return dateVal.dateFormat(this.dateFormat);
36780     },
36781
36782     renderBool : function(bVal){
36783         return bVal ? 'true' : 'false';
36784     },
36785
36786     isCellEditable : function(colIndex, rowIndex){
36787         return colIndex == 1;
36788     },
36789
36790     getRenderer : function(col){
36791         return col == 1 ?
36792             this.renderCellDelegate : this.renderPropDelegate;
36793     },
36794
36795     renderProp : function(v){
36796         return this.getPropertyName(v);
36797     },
36798
36799     renderCell : function(val){
36800         var rv = val;
36801         if(val instanceof Date){
36802             rv = this.renderDate(val);
36803         }else if(typeof val == 'boolean'){
36804             rv = this.renderBool(val);
36805         }
36806         return Roo.util.Format.htmlEncode(rv);
36807     },
36808
36809     getPropertyName : function(name){
36810         var pn = this.grid.propertyNames;
36811         return pn && pn[name] ? pn[name] : name;
36812     },
36813
36814     getCellEditor : function(colIndex, rowIndex){
36815         var p = this.store.getProperty(rowIndex);
36816         var n = p.data['name'], val = p.data['value'];
36817         
36818         if(typeof(this.grid.customEditors[n]) == 'string'){
36819             return this.editors[this.grid.customEditors[n]];
36820         }
36821         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36822             return this.grid.customEditors[n];
36823         }
36824         if(val instanceof Date){
36825             return this.editors['date'];
36826         }else if(typeof val == 'number'){
36827             return this.editors['number'];
36828         }else if(typeof val == 'boolean'){
36829             return this.editors['boolean'];
36830         }else{
36831             return this.editors['string'];
36832         }
36833     }
36834 });
36835
36836 /**
36837  * @class Roo.grid.PropertyGrid
36838  * @extends Roo.grid.EditorGrid
36839  * This class represents the  interface of a component based property grid control.
36840  * <br><br>Usage:<pre><code>
36841  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36842       
36843  });
36844  // set any options
36845  grid.render();
36846  * </code></pre>
36847   
36848  * @constructor
36849  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36850  * The container MUST have some type of size defined for the grid to fill. The container will be
36851  * automatically set to position relative if it isn't already.
36852  * @param {Object} config A config object that sets properties on this grid.
36853  */
36854 Roo.grid.PropertyGrid = function(container, config){
36855     config = config || {};
36856     var store = new Roo.grid.PropertyStore(this);
36857     this.store = store;
36858     var cm = new Roo.grid.PropertyColumnModel(this, store);
36859     store.store.sort('name', 'ASC');
36860     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36861         ds: store.store,
36862         cm: cm,
36863         enableColLock:false,
36864         enableColumnMove:false,
36865         stripeRows:false,
36866         trackMouseOver: false,
36867         clicksToEdit:1
36868     }, config));
36869     this.getGridEl().addClass('x-props-grid');
36870     this.lastEditRow = null;
36871     this.on('columnresize', this.onColumnResize, this);
36872     this.addEvents({
36873          /**
36874              * @event beforepropertychange
36875              * Fires before a property changes (return false to stop?)
36876              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36877              * @param {String} id Record Id
36878              * @param {String} newval New Value
36879          * @param {String} oldval Old Value
36880              */
36881         "beforepropertychange": true,
36882         /**
36883              * @event propertychange
36884              * Fires after a property changes
36885              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36886              * @param {String} id Record Id
36887              * @param {String} newval New Value
36888          * @param {String} oldval Old Value
36889              */
36890         "propertychange": true
36891     });
36892     this.customEditors = this.customEditors || {};
36893 };
36894 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36895     
36896      /**
36897      * @cfg {Object} customEditors map of colnames=> custom editors.
36898      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36899      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36900      * false disables editing of the field.
36901          */
36902     
36903       /**
36904      * @cfg {Object} propertyNames map of property Names to their displayed value
36905          */
36906     
36907     render : function(){
36908         Roo.grid.PropertyGrid.superclass.render.call(this);
36909         this.autoSize.defer(100, this);
36910     },
36911
36912     autoSize : function(){
36913         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36914         if(this.view){
36915             this.view.fitColumns();
36916         }
36917     },
36918
36919     onColumnResize : function(){
36920         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36921         this.autoSize();
36922     },
36923     /**
36924      * Sets the data for the Grid
36925      * accepts a Key => Value object of all the elements avaiable.
36926      * @param {Object} data  to appear in grid.
36927      */
36928     setSource : function(source){
36929         this.store.setSource(source);
36930         //this.autoSize();
36931     },
36932     /**
36933      * Gets all the data from the grid.
36934      * @return {Object} data  data stored in grid
36935      */
36936     getSource : function(){
36937         return this.store.getSource();
36938     }
36939 });/*
36940   
36941  * Licence LGPL
36942  
36943  */
36944  
36945 /**
36946  * @class Roo.grid.Calendar
36947  * @extends Roo.util.Grid
36948  * This class extends the Grid to provide a calendar widget
36949  * <br><br>Usage:<pre><code>
36950  var grid = new Roo.grid.Calendar("my-container-id", {
36951      ds: myDataStore,
36952      cm: myColModel,
36953      selModel: mySelectionModel,
36954      autoSizeColumns: true,
36955      monitorWindowResize: false,
36956      trackMouseOver: true
36957      eventstore : real data store..
36958  });
36959  // set any options
36960  grid.render();
36961   
36962   * @constructor
36963  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36964  * The container MUST have some type of size defined for the grid to fill. The container will be
36965  * automatically set to position relative if it isn't already.
36966  * @param {Object} config A config object that sets properties on this grid.
36967  */
36968 Roo.grid.Calendar = function(container, config){
36969         // initialize the container
36970         this.container = Roo.get(container);
36971         this.container.update("");
36972         this.container.setStyle("overflow", "hidden");
36973     this.container.addClass('x-grid-container');
36974
36975     this.id = this.container.id;
36976
36977     Roo.apply(this, config);
36978     // check and correct shorthanded configs
36979     
36980     var rows = [];
36981     var d =1;
36982     for (var r = 0;r < 6;r++) {
36983         
36984         rows[r]=[];
36985         for (var c =0;c < 7;c++) {
36986             rows[r][c]= '';
36987         }
36988     }
36989     if (this.eventStore) {
36990         this.eventStore= Roo.factory(this.eventStore, Roo.data);
36991         this.eventStore.on('load',this.onLoad, this);
36992         this.eventStore.on('beforeload',this.clearEvents, this);
36993          
36994     }
36995     
36996     this.dataSource = new Roo.data.Store({
36997             proxy: new Roo.data.MemoryProxy(rows),
36998             reader: new Roo.data.ArrayReader({}, [
36999                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37000     });
37001
37002     this.dataSource.load();
37003     this.ds = this.dataSource;
37004     this.ds.xmodule = this.xmodule || false;
37005     
37006     
37007     var cellRender = function(v,x,r)
37008     {
37009         return String.format(
37010             '<div class="fc-day  fc-widget-content"><div>' +
37011                 '<div class="fc-event-container"></div>' +
37012                 '<div class="fc-day-number">{0}</div>'+
37013                 
37014                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37015             '</div></div>', v);
37016     
37017     }
37018     
37019     
37020     this.colModel = new Roo.grid.ColumnModel( [
37021         {
37022             xtype: 'ColumnModel',
37023             xns: Roo.grid,
37024             dataIndex : 'weekday0',
37025             header : 'Sunday',
37026             renderer : cellRender
37027         },
37028         {
37029             xtype: 'ColumnModel',
37030             xns: Roo.grid,
37031             dataIndex : 'weekday1',
37032             header : 'Monday',
37033             renderer : cellRender
37034         },
37035         {
37036             xtype: 'ColumnModel',
37037             xns: Roo.grid,
37038             dataIndex : 'weekday2',
37039             header : 'Tuesday',
37040             renderer : cellRender
37041         },
37042         {
37043             xtype: 'ColumnModel',
37044             xns: Roo.grid,
37045             dataIndex : 'weekday3',
37046             header : 'Wednesday',
37047             renderer : cellRender
37048         },
37049         {
37050             xtype: 'ColumnModel',
37051             xns: Roo.grid,
37052             dataIndex : 'weekday4',
37053             header : 'Thursday',
37054             renderer : cellRender
37055         },
37056         {
37057             xtype: 'ColumnModel',
37058             xns: Roo.grid,
37059             dataIndex : 'weekday5',
37060             header : 'Friday',
37061             renderer : cellRender
37062         },
37063         {
37064             xtype: 'ColumnModel',
37065             xns: Roo.grid,
37066             dataIndex : 'weekday6',
37067             header : 'Saturday',
37068             renderer : cellRender
37069         }
37070     ]);
37071     this.cm = this.colModel;
37072     this.cm.xmodule = this.xmodule || false;
37073  
37074         
37075           
37076     //this.selModel = new Roo.grid.CellSelectionModel();
37077     //this.sm = this.selModel;
37078     //this.selModel.init(this);
37079     
37080     
37081     if(this.width){
37082         this.container.setWidth(this.width);
37083     }
37084
37085     if(this.height){
37086         this.container.setHeight(this.height);
37087     }
37088     /** @private */
37089         this.addEvents({
37090         // raw events
37091         /**
37092          * @event click
37093          * The raw click event for the entire grid.
37094          * @param {Roo.EventObject} e
37095          */
37096         "click" : true,
37097         /**
37098          * @event dblclick
37099          * The raw dblclick event for the entire grid.
37100          * @param {Roo.EventObject} e
37101          */
37102         "dblclick" : true,
37103         /**
37104          * @event contextmenu
37105          * The raw contextmenu event for the entire grid.
37106          * @param {Roo.EventObject} e
37107          */
37108         "contextmenu" : true,
37109         /**
37110          * @event mousedown
37111          * The raw mousedown event for the entire grid.
37112          * @param {Roo.EventObject} e
37113          */
37114         "mousedown" : true,
37115         /**
37116          * @event mouseup
37117          * The raw mouseup event for the entire grid.
37118          * @param {Roo.EventObject} e
37119          */
37120         "mouseup" : true,
37121         /**
37122          * @event mouseover
37123          * The raw mouseover event for the entire grid.
37124          * @param {Roo.EventObject} e
37125          */
37126         "mouseover" : true,
37127         /**
37128          * @event mouseout
37129          * The raw mouseout event for the entire grid.
37130          * @param {Roo.EventObject} e
37131          */
37132         "mouseout" : true,
37133         /**
37134          * @event keypress
37135          * The raw keypress event for the entire grid.
37136          * @param {Roo.EventObject} e
37137          */
37138         "keypress" : true,
37139         /**
37140          * @event keydown
37141          * The raw keydown event for the entire grid.
37142          * @param {Roo.EventObject} e
37143          */
37144         "keydown" : true,
37145
37146         // custom events
37147
37148         /**
37149          * @event cellclick
37150          * Fires when a cell is clicked
37151          * @param {Grid} this
37152          * @param {Number} rowIndex
37153          * @param {Number} columnIndex
37154          * @param {Roo.EventObject} e
37155          */
37156         "cellclick" : true,
37157         /**
37158          * @event celldblclick
37159          * Fires when a cell is double clicked
37160          * @param {Grid} this
37161          * @param {Number} rowIndex
37162          * @param {Number} columnIndex
37163          * @param {Roo.EventObject} e
37164          */
37165         "celldblclick" : true,
37166         /**
37167          * @event rowclick
37168          * Fires when a row is clicked
37169          * @param {Grid} this
37170          * @param {Number} rowIndex
37171          * @param {Roo.EventObject} e
37172          */
37173         "rowclick" : true,
37174         /**
37175          * @event rowdblclick
37176          * Fires when a row is double clicked
37177          * @param {Grid} this
37178          * @param {Number} rowIndex
37179          * @param {Roo.EventObject} e
37180          */
37181         "rowdblclick" : true,
37182         /**
37183          * @event headerclick
37184          * Fires when a header is clicked
37185          * @param {Grid} this
37186          * @param {Number} columnIndex
37187          * @param {Roo.EventObject} e
37188          */
37189         "headerclick" : true,
37190         /**
37191          * @event headerdblclick
37192          * Fires when a header cell is double clicked
37193          * @param {Grid} this
37194          * @param {Number} columnIndex
37195          * @param {Roo.EventObject} e
37196          */
37197         "headerdblclick" : true,
37198         /**
37199          * @event rowcontextmenu
37200          * Fires when a row is right clicked
37201          * @param {Grid} this
37202          * @param {Number} rowIndex
37203          * @param {Roo.EventObject} e
37204          */
37205         "rowcontextmenu" : true,
37206         /**
37207          * @event cellcontextmenu
37208          * Fires when a cell is right clicked
37209          * @param {Grid} this
37210          * @param {Number} rowIndex
37211          * @param {Number} cellIndex
37212          * @param {Roo.EventObject} e
37213          */
37214          "cellcontextmenu" : true,
37215         /**
37216          * @event headercontextmenu
37217          * Fires when a header is right clicked
37218          * @param {Grid} this
37219          * @param {Number} columnIndex
37220          * @param {Roo.EventObject} e
37221          */
37222         "headercontextmenu" : true,
37223         /**
37224          * @event bodyscroll
37225          * Fires when the body element is scrolled
37226          * @param {Number} scrollLeft
37227          * @param {Number} scrollTop
37228          */
37229         "bodyscroll" : true,
37230         /**
37231          * @event columnresize
37232          * Fires when the user resizes a column
37233          * @param {Number} columnIndex
37234          * @param {Number} newSize
37235          */
37236         "columnresize" : true,
37237         /**
37238          * @event columnmove
37239          * Fires when the user moves a column
37240          * @param {Number} oldIndex
37241          * @param {Number} newIndex
37242          */
37243         "columnmove" : true,
37244         /**
37245          * @event startdrag
37246          * Fires when row(s) start being dragged
37247          * @param {Grid} this
37248          * @param {Roo.GridDD} dd The drag drop object
37249          * @param {event} e The raw browser event
37250          */
37251         "startdrag" : true,
37252         /**
37253          * @event enddrag
37254          * Fires when a drag operation is complete
37255          * @param {Grid} this
37256          * @param {Roo.GridDD} dd The drag drop object
37257          * @param {event} e The raw browser event
37258          */
37259         "enddrag" : true,
37260         /**
37261          * @event dragdrop
37262          * Fires when dragged row(s) are dropped on a valid DD target
37263          * @param {Grid} this
37264          * @param {Roo.GridDD} dd The drag drop object
37265          * @param {String} targetId The target drag drop object
37266          * @param {event} e The raw browser event
37267          */
37268         "dragdrop" : true,
37269         /**
37270          * @event dragover
37271          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37272          * @param {Grid} this
37273          * @param {Roo.GridDD} dd The drag drop object
37274          * @param {String} targetId The target drag drop object
37275          * @param {event} e The raw browser event
37276          */
37277         "dragover" : true,
37278         /**
37279          * @event dragenter
37280          *  Fires when the dragged row(s) first cross another DD target while being dragged
37281          * @param {Grid} this
37282          * @param {Roo.GridDD} dd The drag drop object
37283          * @param {String} targetId The target drag drop object
37284          * @param {event} e The raw browser event
37285          */
37286         "dragenter" : true,
37287         /**
37288          * @event dragout
37289          * Fires when the dragged row(s) leave another DD target while being dragged
37290          * @param {Grid} this
37291          * @param {Roo.GridDD} dd The drag drop object
37292          * @param {String} targetId The target drag drop object
37293          * @param {event} e The raw browser event
37294          */
37295         "dragout" : true,
37296         /**
37297          * @event rowclass
37298          * Fires when a row is rendered, so you can change add a style to it.
37299          * @param {GridView} gridview   The grid view
37300          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37301          */
37302         'rowclass' : true,
37303
37304         /**
37305          * @event render
37306          * Fires when the grid is rendered
37307          * @param {Grid} grid
37308          */
37309         'render' : true,
37310             /**
37311              * @event select
37312              * Fires when a date is selected
37313              * @param {DatePicker} this
37314              * @param {Date} date The selected date
37315              */
37316         'select': true,
37317         /**
37318              * @event monthchange
37319              * Fires when the displayed month changes 
37320              * @param {DatePicker} this
37321              * @param {Date} date The selected month
37322              */
37323         'monthchange': true,
37324         /**
37325              * @event evententer
37326              * Fires when mouse over an event
37327              * @param {Calendar} this
37328              * @param {event} Event
37329              */
37330         'evententer': true,
37331         /**
37332              * @event eventleave
37333              * Fires when the mouse leaves an
37334              * @param {Calendar} this
37335              * @param {event}
37336              */
37337         'eventleave': true,
37338         /**
37339              * @event eventclick
37340              * Fires when the mouse click an
37341              * @param {Calendar} this
37342              * @param {event}
37343              */
37344         'eventclick': true,
37345         /**
37346              * @event eventrender
37347              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37348              * @param {Calendar} this
37349              * @param {data} data to be modified
37350              */
37351         'eventrender': true
37352         
37353     });
37354
37355     Roo.grid.Grid.superclass.constructor.call(this);
37356     this.on('render', function() {
37357         this.view.el.addClass('x-grid-cal'); 
37358         
37359         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37360
37361     },this);
37362     
37363     if (!Roo.grid.Calendar.style) {
37364         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37365             
37366             
37367             '.x-grid-cal .x-grid-col' :  {
37368                 height: 'auto !important',
37369                 'vertical-align': 'top'
37370             },
37371             '.x-grid-cal  .fc-event-hori' : {
37372                 height: '14px'
37373             }
37374              
37375             
37376         }, Roo.id());
37377     }
37378
37379     
37380     
37381 };
37382 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37383     /**
37384      * @cfg {Store} eventStore The store that loads events.
37385      */
37386     eventStore : 25,
37387
37388      
37389     activeDate : false,
37390     startDay : 0,
37391     autoWidth : true,
37392     monitorWindowResize : false,
37393
37394     
37395     resizeColumns : function() {
37396         var col = (this.view.el.getWidth() / 7) - 3;
37397         // loop through cols, and setWidth
37398         for(var i =0 ; i < 7 ; i++){
37399             this.cm.setColumnWidth(i, col);
37400         }
37401     },
37402      setDate :function(date) {
37403         
37404         Roo.log('setDate?');
37405         
37406         this.resizeColumns();
37407         var vd = this.activeDate;
37408         this.activeDate = date;
37409 //        if(vd && this.el){
37410 //            var t = date.getTime();
37411 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37412 //                Roo.log('using add remove');
37413 //                
37414 //                this.fireEvent('monthchange', this, date);
37415 //                
37416 //                this.cells.removeClass("fc-state-highlight");
37417 //                this.cells.each(function(c){
37418 //                   if(c.dateValue == t){
37419 //                       c.addClass("fc-state-highlight");
37420 //                       setTimeout(function(){
37421 //                            try{c.dom.firstChild.focus();}catch(e){}
37422 //                       }, 50);
37423 //                       return false;
37424 //                   }
37425 //                   return true;
37426 //                });
37427 //                return;
37428 //            }
37429 //        }
37430         
37431         var days = date.getDaysInMonth();
37432         
37433         var firstOfMonth = date.getFirstDateOfMonth();
37434         var startingPos = firstOfMonth.getDay()-this.startDay;
37435         
37436         if(startingPos < this.startDay){
37437             startingPos += 7;
37438         }
37439         
37440         var pm = date.add(Date.MONTH, -1);
37441         var prevStart = pm.getDaysInMonth()-startingPos;
37442 //        
37443         
37444         
37445         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37446         
37447         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37448         //this.cells.addClassOnOver('fc-state-hover');
37449         
37450         var cells = this.cells.elements;
37451         var textEls = this.textNodes;
37452         
37453         //Roo.each(cells, function(cell){
37454         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37455         //});
37456         
37457         days += startingPos;
37458
37459         // convert everything to numbers so it's fast
37460         var day = 86400000;
37461         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37462         //Roo.log(d);
37463         //Roo.log(pm);
37464         //Roo.log(prevStart);
37465         
37466         var today = new Date().clearTime().getTime();
37467         var sel = date.clearTime().getTime();
37468         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37469         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37470         var ddMatch = this.disabledDatesRE;
37471         var ddText = this.disabledDatesText;
37472         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37473         var ddaysText = this.disabledDaysText;
37474         var format = this.format;
37475         
37476         var setCellClass = function(cal, cell){
37477             
37478             //Roo.log('set Cell Class');
37479             cell.title = "";
37480             var t = d.getTime();
37481             
37482             //Roo.log(d);
37483             
37484             
37485             cell.dateValue = t;
37486             if(t == today){
37487                 cell.className += " fc-today";
37488                 cell.className += " fc-state-highlight";
37489                 cell.title = cal.todayText;
37490             }
37491             if(t == sel){
37492                 // disable highlight in other month..
37493                 cell.className += " fc-state-highlight";
37494                 
37495             }
37496             // disabling
37497             if(t < min) {
37498                 //cell.className = " fc-state-disabled";
37499                 cell.title = cal.minText;
37500                 return;
37501             }
37502             if(t > max) {
37503                 //cell.className = " fc-state-disabled";
37504                 cell.title = cal.maxText;
37505                 return;
37506             }
37507             if(ddays){
37508                 if(ddays.indexOf(d.getDay()) != -1){
37509                     // cell.title = ddaysText;
37510                    // cell.className = " fc-state-disabled";
37511                 }
37512             }
37513             if(ddMatch && format){
37514                 var fvalue = d.dateFormat(format);
37515                 if(ddMatch.test(fvalue)){
37516                     cell.title = ddText.replace("%0", fvalue);
37517                    cell.className = " fc-state-disabled";
37518                 }
37519             }
37520             
37521             if (!cell.initialClassName) {
37522                 cell.initialClassName = cell.dom.className;
37523             }
37524             
37525             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37526         };
37527
37528         var i = 0;
37529         
37530         for(; i < startingPos; i++) {
37531             cells[i].dayName =  (++prevStart);
37532             Roo.log(textEls[i]);
37533             d.setDate(d.getDate()+1);
37534             
37535             //cells[i].className = "fc-past fc-other-month";
37536             setCellClass(this, cells[i]);
37537         }
37538         
37539         var intDay = 0;
37540         
37541         for(; i < days; i++){
37542             intDay = i - startingPos + 1;
37543             cells[i].dayName =  (intDay);
37544             d.setDate(d.getDate()+1);
37545             
37546             cells[i].className = ''; // "x-date-active";
37547             setCellClass(this, cells[i]);
37548         }
37549         var extraDays = 0;
37550         
37551         for(; i < 42; i++) {
37552             //textEls[i].innerHTML = (++extraDays);
37553             
37554             d.setDate(d.getDate()+1);
37555             cells[i].dayName = (++extraDays);
37556             cells[i].className = "fc-future fc-other-month";
37557             setCellClass(this, cells[i]);
37558         }
37559         
37560         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37561         
37562         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37563         
37564         // this will cause all the cells to mis
37565         var rows= [];
37566         var i =0;
37567         for (var r = 0;r < 6;r++) {
37568             for (var c =0;c < 7;c++) {
37569                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37570             }    
37571         }
37572         
37573         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37574         for(i=0;i<cells.length;i++) {
37575             
37576             this.cells.elements[i].dayName = cells[i].dayName ;
37577             this.cells.elements[i].className = cells[i].className;
37578             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37579             this.cells.elements[i].title = cells[i].title ;
37580             this.cells.elements[i].dateValue = cells[i].dateValue ;
37581         }
37582         
37583         
37584         
37585         
37586         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37587         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37588         
37589         ////if(totalRows != 6){
37590             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37591            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37592        // }
37593         
37594         this.fireEvent('monthchange', this, date);
37595         
37596         
37597     },
37598  /**
37599      * Returns the grid's SelectionModel.
37600      * @return {SelectionModel}
37601      */
37602     getSelectionModel : function(){
37603         if(!this.selModel){
37604             this.selModel = new Roo.grid.CellSelectionModel();
37605         }
37606         return this.selModel;
37607     },
37608
37609     load: function() {
37610         this.eventStore.load()
37611         
37612         
37613         
37614     },
37615     
37616     findCell : function(dt) {
37617         dt = dt.clearTime().getTime();
37618         var ret = false;
37619         this.cells.each(function(c){
37620             //Roo.log("check " +c.dateValue + '?=' + dt);
37621             if(c.dateValue == dt){
37622                 ret = c;
37623                 return false;
37624             }
37625             return true;
37626         });
37627         
37628         return ret;
37629     },
37630     
37631     findCells : function(rec) {
37632         var s = rec.data.start_dt.clone().clearTime().getTime();
37633        // Roo.log(s);
37634         var e= rec.data.end_dt.clone().clearTime().getTime();
37635        // Roo.log(e);
37636         var ret = [];
37637         this.cells.each(function(c){
37638              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37639             
37640             if(c.dateValue > e){
37641                 return ;
37642             }
37643             if(c.dateValue < s){
37644                 return ;
37645             }
37646             ret.push(c);
37647         });
37648         
37649         return ret;    
37650     },
37651     
37652     findBestRow: function(cells)
37653     {
37654         var ret = 0;
37655         
37656         for (var i =0 ; i < cells.length;i++) {
37657             ret  = Math.max(cells[i].rows || 0,ret);
37658         }
37659         return ret;
37660         
37661     },
37662     
37663     
37664     addItem : function(rec)
37665     {
37666         // look for vertical location slot in
37667         var cells = this.findCells(rec);
37668         
37669         rec.row = this.findBestRow(cells);
37670         
37671         // work out the location.
37672         
37673         var crow = false;
37674         var rows = [];
37675         for(var i =0; i < cells.length; i++) {
37676             if (!crow) {
37677                 crow = {
37678                     start : cells[i],
37679                     end :  cells[i]
37680                 };
37681                 continue;
37682             }
37683             if (crow.start.getY() == cells[i].getY()) {
37684                 // on same row.
37685                 crow.end = cells[i];
37686                 continue;
37687             }
37688             // different row.
37689             rows.push(crow);
37690             crow = {
37691                 start: cells[i],
37692                 end : cells[i]
37693             };
37694             
37695         }
37696         
37697         rows.push(crow);
37698         rec.els = [];
37699         rec.rows = rows;
37700         rec.cells = cells;
37701         for (var i = 0; i < cells.length;i++) {
37702             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37703             
37704         }
37705         
37706         
37707     },
37708     
37709     clearEvents: function() {
37710         
37711         if (!this.eventStore.getCount()) {
37712             return;
37713         }
37714         // reset number of rows in cells.
37715         Roo.each(this.cells.elements, function(c){
37716             c.rows = 0;
37717         });
37718         
37719         this.eventStore.each(function(e) {
37720             this.clearEvent(e);
37721         },this);
37722         
37723     },
37724     
37725     clearEvent : function(ev)
37726     {
37727         if (ev.els) {
37728             Roo.each(ev.els, function(el) {
37729                 el.un('mouseenter' ,this.onEventEnter, this);
37730                 el.un('mouseleave' ,this.onEventLeave, this);
37731                 el.remove();
37732             },this);
37733             ev.els = [];
37734         }
37735     },
37736     
37737     
37738     renderEvent : function(ev,ctr) {
37739         if (!ctr) {
37740              ctr = this.view.el.select('.fc-event-container',true).first();
37741         }
37742         
37743          
37744         this.clearEvent(ev);
37745             //code
37746        
37747         
37748         
37749         ev.els = [];
37750         var cells = ev.cells;
37751         var rows = ev.rows;
37752         this.fireEvent('eventrender', this, ev);
37753         
37754         for(var i =0; i < rows.length; i++) {
37755             
37756             cls = '';
37757             if (i == 0) {
37758                 cls += ' fc-event-start';
37759             }
37760             if ((i+1) == rows.length) {
37761                 cls += ' fc-event-end';
37762             }
37763             
37764             //Roo.log(ev.data);
37765             // how many rows should it span..
37766             var cg = this.eventTmpl.append(ctr,Roo.apply({
37767                 fccls : cls
37768                 
37769             }, ev.data) , true);
37770             
37771             
37772             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37773             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37774             cg.on('click', this.onEventClick, this, ev);
37775             
37776             ev.els.push(cg);
37777             
37778             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37779             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37780             //Roo.log(cg);
37781              
37782             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37783             cg.setWidth(ebox.right - sbox.x -2);
37784         }
37785     },
37786     
37787     renderEvents: function()
37788     {   
37789         // first make sure there is enough space..
37790         
37791         if (!this.eventTmpl) {
37792             this.eventTmpl = new Roo.Template(
37793                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37794                     '<div class="fc-event-inner">' +
37795                         '<span class="fc-event-time">{time}</span>' +
37796                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37797                     '</div>' +
37798                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37799                 '</div>'
37800             );
37801                 
37802         }
37803                
37804         
37805         
37806         this.cells.each(function(c) {
37807             //Roo.log(c.select('.fc-day-content div',true).first());
37808             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37809         });
37810         
37811         var ctr = this.view.el.select('.fc-event-container',true).first();
37812         
37813         var cls;
37814         this.eventStore.each(function(ev){
37815             
37816             this.renderEvent(ev);
37817              
37818              
37819         }, this);
37820         this.view.layout();
37821         
37822     },
37823     
37824     onEventEnter: function (e, el,event,d) {
37825         this.fireEvent('evententer', this, el, event);
37826     },
37827     
37828     onEventLeave: function (e, el,event,d) {
37829         this.fireEvent('eventleave', this, el, event);
37830     },
37831     
37832     onEventClick: function (e, el,event,d) {
37833         this.fireEvent('eventclick', this, el, event);
37834     },
37835     
37836     onMonthChange: function () {
37837         this.store.load();
37838     },
37839     
37840     onLoad: function () {
37841         
37842         //Roo.log('calendar onload');
37843 //         
37844         if(this.eventStore.getCount() > 0){
37845             
37846            
37847             
37848             this.eventStore.each(function(d){
37849                 
37850                 
37851                 // FIXME..
37852                 var add =   d.data;
37853                 if (typeof(add.end_dt) == 'undefined')  {
37854                     Roo.log("Missing End time in calendar data: ");
37855                     Roo.log(d);
37856                     return;
37857                 }
37858                 if (typeof(add.start_dt) == 'undefined')  {
37859                     Roo.log("Missing Start time in calendar data: ");
37860                     Roo.log(d);
37861                     return;
37862                 }
37863                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37864                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37865                 add.id = add.id || d.id;
37866                 add.title = add.title || '??';
37867                 
37868                 this.addItem(d);
37869                 
37870              
37871             },this);
37872         }
37873         
37874         this.renderEvents();
37875     }
37876     
37877
37878 });
37879 /*
37880  grid : {
37881                 xtype: 'Grid',
37882                 xns: Roo.grid,
37883                 listeners : {
37884                     render : function ()
37885                     {
37886                         _this.grid = this;
37887                         
37888                         if (!this.view.el.hasClass('course-timesheet')) {
37889                             this.view.el.addClass('course-timesheet');
37890                         }
37891                         if (this.tsStyle) {
37892                             this.ds.load({});
37893                             return; 
37894                         }
37895                         Roo.log('width');
37896                         Roo.log(_this.grid.view.el.getWidth());
37897                         
37898                         
37899                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37900                             '.course-timesheet .x-grid-row' : {
37901                                 height: '80px'
37902                             },
37903                             '.x-grid-row td' : {
37904                                 'vertical-align' : 0
37905                             },
37906                             '.course-edit-link' : {
37907                                 'color' : 'blue',
37908                                 'text-overflow' : 'ellipsis',
37909                                 'overflow' : 'hidden',
37910                                 'white-space' : 'nowrap',
37911                                 'cursor' : 'pointer'
37912                             },
37913                             '.sub-link' : {
37914                                 'color' : 'green'
37915                             },
37916                             '.de-act-sup-link' : {
37917                                 'color' : 'purple',
37918                                 'text-decoration' : 'line-through'
37919                             },
37920                             '.de-act-link' : {
37921                                 'color' : 'red',
37922                                 'text-decoration' : 'line-through'
37923                             },
37924                             '.course-timesheet .course-highlight' : {
37925                                 'border-top-style': 'dashed !important',
37926                                 'border-bottom-bottom': 'dashed !important'
37927                             },
37928                             '.course-timesheet .course-item' : {
37929                                 'font-family'   : 'tahoma, arial, helvetica',
37930                                 'font-size'     : '11px',
37931                                 'overflow'      : 'hidden',
37932                                 'padding-left'  : '10px',
37933                                 'padding-right' : '10px',
37934                                 'padding-top' : '10px' 
37935                             }
37936                             
37937                         }, Roo.id());
37938                                 this.ds.load({});
37939                     }
37940                 },
37941                 autoWidth : true,
37942                 monitorWindowResize : false,
37943                 cellrenderer : function(v,x,r)
37944                 {
37945                     return v;
37946                 },
37947                 sm : {
37948                     xtype: 'CellSelectionModel',
37949                     xns: Roo.grid
37950                 },
37951                 dataSource : {
37952                     xtype: 'Store',
37953                     xns: Roo.data,
37954                     listeners : {
37955                         beforeload : function (_self, options)
37956                         {
37957                             options.params = options.params || {};
37958                             options.params._month = _this.monthField.getValue();
37959                             options.params.limit = 9999;
37960                             options.params['sort'] = 'when_dt';    
37961                             options.params['dir'] = 'ASC';    
37962                             this.proxy.loadResponse = this.loadResponse;
37963                             Roo.log("load?");
37964                             //this.addColumns();
37965                         },
37966                         load : function (_self, records, options)
37967                         {
37968                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37969                                 // if you click on the translation.. you can edit it...
37970                                 var el = Roo.get(this);
37971                                 var id = el.dom.getAttribute('data-id');
37972                                 var d = el.dom.getAttribute('data-date');
37973                                 var t = el.dom.getAttribute('data-time');
37974                                 //var id = this.child('span').dom.textContent;
37975                                 
37976                                 //Roo.log(this);
37977                                 Pman.Dialog.CourseCalendar.show({
37978                                     id : id,
37979                                     when_d : d,
37980                                     when_t : t,
37981                                     productitem_active : id ? 1 : 0
37982                                 }, function() {
37983                                     _this.grid.ds.load({});
37984                                 });
37985                            
37986                            });
37987                            
37988                            _this.panel.fireEvent('resize', [ '', '' ]);
37989                         }
37990                     },
37991                     loadResponse : function(o, success, response){
37992                             // this is overridden on before load..
37993                             
37994                             Roo.log("our code?");       
37995                             //Roo.log(success);
37996                             //Roo.log(response)
37997                             delete this.activeRequest;
37998                             if(!success){
37999                                 this.fireEvent("loadexception", this, o, response);
38000                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38001                                 return;
38002                             }
38003                             var result;
38004                             try {
38005                                 result = o.reader.read(response);
38006                             }catch(e){
38007                                 Roo.log("load exception?");
38008                                 this.fireEvent("loadexception", this, o, response, e);
38009                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38010                                 return;
38011                             }
38012                             Roo.log("ready...");        
38013                             // loop through result.records;
38014                             // and set this.tdate[date] = [] << array of records..
38015                             _this.tdata  = {};
38016                             Roo.each(result.records, function(r){
38017                                 //Roo.log(r.data);
38018                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38019                                     _this.tdata[r.data.when_dt.format('j')] = [];
38020                                 }
38021                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38022                             });
38023                             
38024                             //Roo.log(_this.tdata);
38025                             
38026                             result.records = [];
38027                             result.totalRecords = 6;
38028                     
38029                             // let's generate some duumy records for the rows.
38030                             //var st = _this.dateField.getValue();
38031                             
38032                             // work out monday..
38033                             //st = st.add(Date.DAY, -1 * st.format('w'));
38034                             
38035                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38036                             
38037                             var firstOfMonth = date.getFirstDayOfMonth();
38038                             var days = date.getDaysInMonth();
38039                             var d = 1;
38040                             var firstAdded = false;
38041                             for (var i = 0; i < result.totalRecords ; i++) {
38042                                 //var d= st.add(Date.DAY, i);
38043                                 var row = {};
38044                                 var added = 0;
38045                                 for(var w = 0 ; w < 7 ; w++){
38046                                     if(!firstAdded && firstOfMonth != w){
38047                                         continue;
38048                                     }
38049                                     if(d > days){
38050                                         continue;
38051                                     }
38052                                     firstAdded = true;
38053                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38054                                     row['weekday'+w] = String.format(
38055                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38056                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38057                                                     d,
38058                                                     date.format('Y-m-')+dd
38059                                                 );
38060                                     added++;
38061                                     if(typeof(_this.tdata[d]) != 'undefined'){
38062                                         Roo.each(_this.tdata[d], function(r){
38063                                             var is_sub = '';
38064                                             var deactive = '';
38065                                             var id = r.id;
38066                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38067                                             if(r.parent_id*1>0){
38068                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38069                                                 id = r.parent_id;
38070                                             }
38071                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38072                                                 deactive = 'de-act-link';
38073                                             }
38074                                             
38075                                             row['weekday'+w] += String.format(
38076                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38077                                                     id, //0
38078                                                     r.product_id_name, //1
38079                                                     r.when_dt.format('h:ia'), //2
38080                                                     is_sub, //3
38081                                                     deactive, //4
38082                                                     desc // 5
38083                                             );
38084                                         });
38085                                     }
38086                                     d++;
38087                                 }
38088                                 
38089                                 // only do this if something added..
38090                                 if(added > 0){ 
38091                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38092                                 }
38093                                 
38094                                 
38095                                 // push it twice. (second one with an hour..
38096                                 
38097                             }
38098                             //Roo.log(result);
38099                             this.fireEvent("load", this, o, o.request.arg);
38100                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38101                         },
38102                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38103                     proxy : {
38104                         xtype: 'HttpProxy',
38105                         xns: Roo.data,
38106                         method : 'GET',
38107                         url : baseURL + '/Roo/Shop_course.php'
38108                     },
38109                     reader : {
38110                         xtype: 'JsonReader',
38111                         xns: Roo.data,
38112                         id : 'id',
38113                         fields : [
38114                             {
38115                                 'name': 'id',
38116                                 'type': 'int'
38117                             },
38118                             {
38119                                 'name': 'when_dt',
38120                                 'type': 'string'
38121                             },
38122                             {
38123                                 'name': 'end_dt',
38124                                 'type': 'string'
38125                             },
38126                             {
38127                                 'name': 'parent_id',
38128                                 'type': 'int'
38129                             },
38130                             {
38131                                 'name': 'product_id',
38132                                 'type': 'int'
38133                             },
38134                             {
38135                                 'name': 'productitem_id',
38136                                 'type': 'int'
38137                             },
38138                             {
38139                                 'name': 'guid',
38140                                 'type': 'int'
38141                             }
38142                         ]
38143                     }
38144                 },
38145                 toolbar : {
38146                     xtype: 'Toolbar',
38147                     xns: Roo,
38148                     items : [
38149                         {
38150                             xtype: 'Button',
38151                             xns: Roo.Toolbar,
38152                             listeners : {
38153                                 click : function (_self, e)
38154                                 {
38155                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38156                                     sd.setMonth(sd.getMonth()-1);
38157                                     _this.monthField.setValue(sd.format('Y-m-d'));
38158                                     _this.grid.ds.load({});
38159                                 }
38160                             },
38161                             text : "Back"
38162                         },
38163                         {
38164                             xtype: 'Separator',
38165                             xns: Roo.Toolbar
38166                         },
38167                         {
38168                             xtype: 'MonthField',
38169                             xns: Roo.form,
38170                             listeners : {
38171                                 render : function (_self)
38172                                 {
38173                                     _this.monthField = _self;
38174                                    // _this.monthField.set  today
38175                                 },
38176                                 select : function (combo, date)
38177                                 {
38178                                     _this.grid.ds.load({});
38179                                 }
38180                             },
38181                             value : (function() { return new Date(); })()
38182                         },
38183                         {
38184                             xtype: 'Separator',
38185                             xns: Roo.Toolbar
38186                         },
38187                         {
38188                             xtype: 'TextItem',
38189                             xns: Roo.Toolbar,
38190                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38191                         },
38192                         {
38193                             xtype: 'Fill',
38194                             xns: Roo.Toolbar
38195                         },
38196                         {
38197                             xtype: 'Button',
38198                             xns: Roo.Toolbar,
38199                             listeners : {
38200                                 click : function (_self, e)
38201                                 {
38202                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38203                                     sd.setMonth(sd.getMonth()+1);
38204                                     _this.monthField.setValue(sd.format('Y-m-d'));
38205                                     _this.grid.ds.load({});
38206                                 }
38207                             },
38208                             text : "Next"
38209                         }
38210                     ]
38211                 },
38212                  
38213             }
38214         };
38215         
38216         *//*
38217  * Based on:
38218  * Ext JS Library 1.1.1
38219  * Copyright(c) 2006-2007, Ext JS, LLC.
38220  *
38221  * Originally Released Under LGPL - original licence link has changed is not relivant.
38222  *
38223  * Fork - LGPL
38224  * <script type="text/javascript">
38225  */
38226  
38227 /**
38228  * @class Roo.LoadMask
38229  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38230  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38231  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38232  * element's UpdateManager load indicator and will be destroyed after the initial load.
38233  * @constructor
38234  * Create a new LoadMask
38235  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38236  * @param {Object} config The config object
38237  */
38238 Roo.LoadMask = function(el, config){
38239     this.el = Roo.get(el);
38240     Roo.apply(this, config);
38241     if(this.store){
38242         this.store.on('beforeload', this.onBeforeLoad, this);
38243         this.store.on('load', this.onLoad, this);
38244         this.store.on('loadexception', this.onLoadException, this);
38245         this.removeMask = false;
38246     }else{
38247         var um = this.el.getUpdateManager();
38248         um.showLoadIndicator = false; // disable the default indicator
38249         um.on('beforeupdate', this.onBeforeLoad, this);
38250         um.on('update', this.onLoad, this);
38251         um.on('failure', this.onLoad, this);
38252         this.removeMask = true;
38253     }
38254 };
38255
38256 Roo.LoadMask.prototype = {
38257     /**
38258      * @cfg {Boolean} removeMask
38259      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38260      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38261      */
38262     /**
38263      * @cfg {String} msg
38264      * The text to display in a centered loading message box (defaults to 'Loading...')
38265      */
38266     msg : 'Loading...',
38267     /**
38268      * @cfg {String} msgCls
38269      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38270      */
38271     msgCls : 'x-mask-loading',
38272
38273     /**
38274      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38275      * @type Boolean
38276      */
38277     disabled: false,
38278
38279     /**
38280      * Disables the mask to prevent it from being displayed
38281      */
38282     disable : function(){
38283        this.disabled = true;
38284     },
38285
38286     /**
38287      * Enables the mask so that it can be displayed
38288      */
38289     enable : function(){
38290         this.disabled = false;
38291     },
38292     
38293     onLoadException : function()
38294     {
38295         Roo.log(arguments);
38296         
38297         if (typeof(arguments[3]) != 'undefined') {
38298             Roo.MessageBox.alert("Error loading",arguments[3]);
38299         } 
38300         /*
38301         try {
38302             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38303                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38304             }   
38305         } catch(e) {
38306             
38307         }
38308         */
38309     
38310         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38311     },
38312     // private
38313     onLoad : function()
38314     {
38315         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38316     },
38317
38318     // private
38319     onBeforeLoad : function(){
38320         if(!this.disabled){
38321             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38322         }
38323     },
38324
38325     // private
38326     destroy : function(){
38327         if(this.store){
38328             this.store.un('beforeload', this.onBeforeLoad, this);
38329             this.store.un('load', this.onLoad, this);
38330             this.store.un('loadexception', this.onLoadException, this);
38331         }else{
38332             var um = this.el.getUpdateManager();
38333             um.un('beforeupdate', this.onBeforeLoad, this);
38334             um.un('update', this.onLoad, this);
38335             um.un('failure', this.onLoad, this);
38336         }
38337     }
38338 };/*
38339  * Based on:
38340  * Ext JS Library 1.1.1
38341  * Copyright(c) 2006-2007, Ext JS, LLC.
38342  *
38343  * Originally Released Under LGPL - original licence link has changed is not relivant.
38344  *
38345  * Fork - LGPL
38346  * <script type="text/javascript">
38347  */
38348
38349
38350 /**
38351  * @class Roo.XTemplate
38352  * @extends Roo.Template
38353  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38354 <pre><code>
38355 var t = new Roo.XTemplate(
38356         '&lt;select name="{name}"&gt;',
38357                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38358         '&lt;/select&gt;'
38359 );
38360  
38361 // then append, applying the master template values
38362  </code></pre>
38363  *
38364  * Supported features:
38365  *
38366  *  Tags:
38367
38368 <pre><code>
38369       {a_variable} - output encoded.
38370       {a_variable.format:("Y-m-d")} - call a method on the variable
38371       {a_variable:raw} - unencoded output
38372       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38373       {a_variable:this.method_on_template(...)} - call a method on the template object.
38374  
38375 </code></pre>
38376  *  The tpl tag:
38377 <pre><code>
38378         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38379         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38380         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38381         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38382   
38383         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38384         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38385 </code></pre>
38386  *      
38387  */
38388 Roo.XTemplate = function()
38389 {
38390     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38391     if (this.html) {
38392         this.compile();
38393     }
38394 };
38395
38396
38397 Roo.extend(Roo.XTemplate, Roo.Template, {
38398
38399     /**
38400      * The various sub templates
38401      */
38402     tpls : false,
38403     /**
38404      *
38405      * basic tag replacing syntax
38406      * WORD:WORD()
38407      *
38408      * // you can fake an object call by doing this
38409      *  x.t:(test,tesT) 
38410      * 
38411      */
38412     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38413
38414     /**
38415      * compile the template
38416      *
38417      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38418      *
38419      */
38420     compile: function()
38421     {
38422         var s = this.html;
38423      
38424         s = ['<tpl>', s, '</tpl>'].join('');
38425     
38426         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38427             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38428             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38429             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38430             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38431             m,
38432             id     = 0,
38433             tpls   = [];
38434     
38435         while(true == !!(m = s.match(re))){
38436             var forMatch   = m[0].match(nameRe),
38437                 ifMatch   = m[0].match(ifRe),
38438                 execMatch   = m[0].match(execRe),
38439                 namedMatch   = m[0].match(namedRe),
38440                 
38441                 exp  = null, 
38442                 fn   = null,
38443                 exec = null,
38444                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38445                 
38446             if (ifMatch) {
38447                 // if - puts fn into test..
38448                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38449                 if(exp){
38450                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38451                 }
38452             }
38453             
38454             if (execMatch) {
38455                 // exec - calls a function... returns empty if true is  returned.
38456                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38457                 if(exp){
38458                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38459                 }
38460             }
38461             
38462             
38463             if (name) {
38464                 // for = 
38465                 switch(name){
38466                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38467                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38468                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38469                 }
38470             }
38471             var uid = namedMatch ? namedMatch[1] : id;
38472             
38473             
38474             tpls.push({
38475                 id:     namedMatch ? namedMatch[1] : id,
38476                 target: name,
38477                 exec:   exec,
38478                 test:   fn,
38479                 body:   m[1] || ''
38480             });
38481             if (namedMatch) {
38482                 s = s.replace(m[0], '');
38483             } else { 
38484                 s = s.replace(m[0], '{xtpl'+ id + '}');
38485             }
38486             ++id;
38487         }
38488         this.tpls = [];
38489         for(var i = tpls.length-1; i >= 0; --i){
38490             this.compileTpl(tpls[i]);
38491             this.tpls[tpls[i].id] = tpls[i];
38492         }
38493         this.master = tpls[tpls.length-1];
38494         return this;
38495     },
38496     /**
38497      * same as applyTemplate, except it's done to one of the subTemplates
38498      * when using named templates, you can do:
38499      *
38500      * var str = pl.applySubTemplate('your-name', values);
38501      *
38502      * 
38503      * @param {Number} id of the template
38504      * @param {Object} values to apply to template
38505      * @param {Object} parent (normaly the instance of this object)
38506      */
38507     applySubTemplate : function(id, values, parent)
38508     {
38509         
38510         
38511         var t = this.tpls[id];
38512         
38513         
38514         try { 
38515             if(t.test && !t.test.call(this, values, parent)){
38516                 return '';
38517             }
38518         } catch(e) {
38519             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38520             Roo.log(e.toString());
38521             Roo.log(t.test);
38522             return ''
38523         }
38524         try { 
38525             
38526             if(t.exec && t.exec.call(this, values, parent)){
38527                 return '';
38528             }
38529         } catch(e) {
38530             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38531             Roo.log(e.toString());
38532             Roo.log(t.exec);
38533             return ''
38534         }
38535         try {
38536             var vs = t.target ? t.target.call(this, values, parent) : values;
38537             parent = t.target ? values : parent;
38538             if(t.target && vs instanceof Array){
38539                 var buf = [];
38540                 for(var i = 0, len = vs.length; i < len; i++){
38541                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38542                 }
38543                 return buf.join('');
38544             }
38545             return t.compiled.call(this, vs, parent);
38546         } catch (e) {
38547             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38548             Roo.log(e.toString());
38549             Roo.log(t.compiled);
38550             return '';
38551         }
38552     },
38553
38554     compileTpl : function(tpl)
38555     {
38556         var fm = Roo.util.Format;
38557         var useF = this.disableFormats !== true;
38558         var sep = Roo.isGecko ? "+" : ",";
38559         var undef = function(str) {
38560             Roo.log("Property not found :"  + str);
38561             return '';
38562         };
38563         
38564         var fn = function(m, name, format, args)
38565         {
38566             //Roo.log(arguments);
38567             args = args ? args.replace(/\\'/g,"'") : args;
38568             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38569             if (typeof(format) == 'undefined') {
38570                 format= 'htmlEncode';
38571             }
38572             if (format == 'raw' ) {
38573                 format = false;
38574             }
38575             
38576             if(name.substr(0, 4) == 'xtpl'){
38577                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38578             }
38579             
38580             // build an array of options to determine if value is undefined..
38581             
38582             // basically get 'xxxx.yyyy' then do
38583             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38584             //    (function () { Roo.log("Property not found"); return ''; })() :
38585             //    ......
38586             
38587             var udef_ar = [];
38588             var lookfor = '';
38589             Roo.each(name.split('.'), function(st) {
38590                 lookfor += (lookfor.length ? '.': '') + st;
38591                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38592             });
38593             
38594             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38595             
38596             
38597             if(format && useF){
38598                 
38599                 args = args ? ',' + args : "";
38600                  
38601                 if(format.substr(0, 5) != "this."){
38602                     format = "fm." + format + '(';
38603                 }else{
38604                     format = 'this.call("'+ format.substr(5) + '", ';
38605                     args = ", values";
38606                 }
38607                 
38608                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38609             }
38610              
38611             if (args.length) {
38612                 // called with xxyx.yuu:(test,test)
38613                 // change to ()
38614                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38615             }
38616             // raw.. - :raw modifier..
38617             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38618             
38619         };
38620         var body;
38621         // branched to use + in gecko and [].join() in others
38622         if(Roo.isGecko){
38623             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38624                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38625                     "';};};";
38626         }else{
38627             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38628             body.push(tpl.body.replace(/(\r\n|\n)/g,
38629                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38630             body.push("'].join('');};};");
38631             body = body.join('');
38632         }
38633         
38634         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38635        
38636         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38637         eval(body);
38638         
38639         return this;
38640     },
38641
38642     applyTemplate : function(values){
38643         return this.master.compiled.call(this, values, {});
38644         //var s = this.subs;
38645     },
38646
38647     apply : function(){
38648         return this.applyTemplate.apply(this, arguments);
38649     }
38650
38651  });
38652
38653 Roo.XTemplate.from = function(el){
38654     el = Roo.getDom(el);
38655     return new Roo.XTemplate(el.value || el.innerHTML);
38656 };