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      * setHTML
15910      * @param {String} html update the Contents of the element.
15911      */
15912     setHTML : function(html)
15913     {
15914         this.fieldEl.dom.innerHTML = html;
15915     }
15916     
15917 });/*
15918  * Based on:
15919  * Ext JS Library 1.1.1
15920  * Copyright(c) 2006-2007, Ext JS, LLC.
15921  *
15922  * Originally Released Under LGPL - original licence link has changed is not relivant.
15923  *
15924  * Fork - LGPL
15925  * <script type="text/javascript">
15926  */
15927  
15928 /**
15929  * @class Roo.form.Field
15930  * @extends Roo.BoxComponent
15931  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
15932  * @constructor
15933  * Creates a new Field
15934  * @param {Object} config Configuration options
15935  */
15936 Roo.form.Field = function(config){
15937     Roo.form.Field.superclass.constructor.call(this, config);
15938 };
15939
15940 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
15941     /**
15942      * @cfg {String} fieldLabel Label to use when rendering a form.
15943      */
15944        /**
15945      * @cfg {String} qtip Mouse over tip
15946      */
15947      
15948     /**
15949      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
15950      */
15951     invalidClass : "x-form-invalid",
15952     /**
15953      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
15954      */
15955     invalidText : "The value in this field is invalid",
15956     /**
15957      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
15958      */
15959     focusClass : "x-form-focus",
15960     /**
15961      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
15962       automatic validation (defaults to "keyup").
15963      */
15964     validationEvent : "keyup",
15965     /**
15966      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
15967      */
15968     validateOnBlur : true,
15969     /**
15970      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
15971      */
15972     validationDelay : 250,
15973     /**
15974      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
15975      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
15976      */
15977     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
15978     /**
15979      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
15980      */
15981     fieldClass : "x-form-field",
15982     /**
15983      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
15984      *<pre>
15985 Value         Description
15986 -----------   ----------------------------------------------------------------------
15987 qtip          Display a quick tip when the user hovers over the field
15988 title         Display a default browser title attribute popup
15989 under         Add a block div beneath the field containing the error text
15990 side          Add an error icon to the right of the field with a popup on hover
15991 [element id]  Add the error text directly to the innerHTML of the specified element
15992 </pre>
15993      */
15994     msgTarget : 'qtip',
15995     /**
15996      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
15997      */
15998     msgFx : 'normal',
15999
16000     /**
16001      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
16002      */
16003     readOnly : false,
16004
16005     /**
16006      * @cfg {Boolean} disabled True to disable the field (defaults to false).
16007      */
16008     disabled : false,
16009
16010     /**
16011      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
16012      */
16013     inputType : undefined,
16014     
16015     /**
16016      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
16017          */
16018         tabIndex : undefined,
16019         
16020     // private
16021     isFormField : true,
16022
16023     // private
16024     hasFocus : false,
16025     /**
16026      * @property {Roo.Element} fieldEl
16027      * Element Containing the rendered Field (with label etc.)
16028      */
16029     /**
16030      * @cfg {Mixed} value A value to initialize this field with.
16031      */
16032     value : undefined,
16033
16034     /**
16035      * @cfg {String} name The field's HTML name attribute.
16036      */
16037     /**
16038      * @cfg {String} cls A CSS class to apply to the field's underlying element.
16039      */
16040     // private
16041     loadedValue : false,
16042      
16043      
16044         // private ??
16045         initComponent : function(){
16046         Roo.form.Field.superclass.initComponent.call(this);
16047         this.addEvents({
16048             /**
16049              * @event focus
16050              * Fires when this field receives input focus.
16051              * @param {Roo.form.Field} this
16052              */
16053             focus : true,
16054             /**
16055              * @event blur
16056              * Fires when this field loses input focus.
16057              * @param {Roo.form.Field} this
16058              */
16059             blur : true,
16060             /**
16061              * @event specialkey
16062              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
16063              * {@link Roo.EventObject#getKey} to determine which key was pressed.
16064              * @param {Roo.form.Field} this
16065              * @param {Roo.EventObject} e The event object
16066              */
16067             specialkey : true,
16068             /**
16069              * @event change
16070              * Fires just before the field blurs if the field value has changed.
16071              * @param {Roo.form.Field} this
16072              * @param {Mixed} newValue The new value
16073              * @param {Mixed} oldValue The original value
16074              */
16075             change : true,
16076             /**
16077              * @event invalid
16078              * Fires after the field has been marked as invalid.
16079              * @param {Roo.form.Field} this
16080              * @param {String} msg The validation message
16081              */
16082             invalid : true,
16083             /**
16084              * @event valid
16085              * Fires after the field has been validated with no errors.
16086              * @param {Roo.form.Field} this
16087              */
16088             valid : true,
16089              /**
16090              * @event keyup
16091              * Fires after the key up
16092              * @param {Roo.form.Field} this
16093              * @param {Roo.EventObject}  e The event Object
16094              */
16095             keyup : true
16096         });
16097     },
16098
16099     /**
16100      * Returns the name attribute of the field if available
16101      * @return {String} name The field name
16102      */
16103     getName: function(){
16104          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
16105     },
16106
16107     // private
16108     onRender : function(ct, position){
16109         Roo.form.Field.superclass.onRender.call(this, ct, position);
16110         if(!this.el){
16111             var cfg = this.getAutoCreate();
16112             if(!cfg.name){
16113                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
16114             }
16115             if (!cfg.name.length) {
16116                 delete cfg.name;
16117             }
16118             if(this.inputType){
16119                 cfg.type = this.inputType;
16120             }
16121             this.el = ct.createChild(cfg, position);
16122         }
16123         var type = this.el.dom.type;
16124         if(type){
16125             if(type == 'password'){
16126                 type = 'text';
16127             }
16128             this.el.addClass('x-form-'+type);
16129         }
16130         if(this.readOnly){
16131             this.el.dom.readOnly = true;
16132         }
16133         if(this.tabIndex !== undefined){
16134             this.el.dom.setAttribute('tabIndex', this.tabIndex);
16135         }
16136
16137         this.el.addClass([this.fieldClass, this.cls]);
16138         this.initValue();
16139     },
16140
16141     /**
16142      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
16143      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
16144      * @return {Roo.form.Field} this
16145      */
16146     applyTo : function(target){
16147         this.allowDomMove = false;
16148         this.el = Roo.get(target);
16149         this.render(this.el.dom.parentNode);
16150         return this;
16151     },
16152
16153     // private
16154     initValue : function(){
16155         if(this.value !== undefined){
16156             this.setValue(this.value);
16157         }else if(this.el.dom.value.length > 0){
16158             this.setValue(this.el.dom.value);
16159         }
16160     },
16161
16162     /**
16163      * Returns true if this field has been changed since it was originally loaded and is not disabled.
16164      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
16165      */
16166     isDirty : function() {
16167         if(this.disabled) {
16168             return false;
16169         }
16170         return String(this.getValue()) !== String(this.originalValue);
16171     },
16172
16173     /**
16174      * stores the current value in loadedValue
16175      */
16176     resetHasChanged : function()
16177     {
16178         this.loadedValue = String(this.getValue());
16179     },
16180     /**
16181      * checks the current value against the 'loaded' value.
16182      * Note - will return false if 'resetHasChanged' has not been called first.
16183      */
16184     hasChanged : function()
16185     {
16186         if(this.disabled || this.readOnly) {
16187             return false;
16188         }
16189         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
16190     },
16191     
16192     
16193     
16194     // private
16195     afterRender : function(){
16196         Roo.form.Field.superclass.afterRender.call(this);
16197         this.initEvents();
16198     },
16199
16200     // private
16201     fireKey : function(e){
16202         //Roo.log('field ' + e.getKey());
16203         if(e.isNavKeyPress()){
16204             this.fireEvent("specialkey", this, e);
16205         }
16206     },
16207
16208     /**
16209      * Resets the current field value to the originally loaded value and clears any validation messages
16210      */
16211     reset : function(){
16212         this.setValue(this.resetValue);
16213         this.originalValue = this.getValue();
16214         this.clearInvalid();
16215     },
16216
16217     // private
16218     initEvents : function(){
16219         // safari killled keypress - so keydown is now used..
16220         this.el.on("keydown" , this.fireKey,  this);
16221         this.el.on("focus", this.onFocus,  this);
16222         this.el.on("blur", this.onBlur,  this);
16223         this.el.relayEvent('keyup', this);
16224
16225         // reference to original value for reset
16226         this.originalValue = this.getValue();
16227         this.resetValue =  this.getValue();
16228     },
16229
16230     // private
16231     onFocus : function(){
16232         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16233             this.el.addClass(this.focusClass);
16234         }
16235         if(!this.hasFocus){
16236             this.hasFocus = true;
16237             this.startValue = this.getValue();
16238             this.fireEvent("focus", this);
16239         }
16240     },
16241
16242     beforeBlur : Roo.emptyFn,
16243
16244     // private
16245     onBlur : function(){
16246         this.beforeBlur();
16247         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
16248             this.el.removeClass(this.focusClass);
16249         }
16250         this.hasFocus = false;
16251         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
16252             this.validate();
16253         }
16254         var v = this.getValue();
16255         if(String(v) !== String(this.startValue)){
16256             this.fireEvent('change', this, v, this.startValue);
16257         }
16258         this.fireEvent("blur", this);
16259     },
16260
16261     /**
16262      * Returns whether or not the field value is currently valid
16263      * @param {Boolean} preventMark True to disable marking the field invalid
16264      * @return {Boolean} True if the value is valid, else false
16265      */
16266     isValid : function(preventMark){
16267         if(this.disabled){
16268             return true;
16269         }
16270         var restore = this.preventMark;
16271         this.preventMark = preventMark === true;
16272         var v = this.validateValue(this.processValue(this.getRawValue()));
16273         this.preventMark = restore;
16274         return v;
16275     },
16276
16277     /**
16278      * Validates the field value
16279      * @return {Boolean} True if the value is valid, else false
16280      */
16281     validate : function(){
16282         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
16283             this.clearInvalid();
16284             return true;
16285         }
16286         return false;
16287     },
16288
16289     processValue : function(value){
16290         return value;
16291     },
16292
16293     // private
16294     // Subclasses should provide the validation implementation by overriding this
16295     validateValue : function(value){
16296         return true;
16297     },
16298
16299     /**
16300      * Mark this field as invalid
16301      * @param {String} msg The validation message
16302      */
16303     markInvalid : function(msg){
16304         if(!this.rendered || this.preventMark){ // not rendered
16305             return;
16306         }
16307         
16308         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16309         
16310         obj.el.addClass(this.invalidClass);
16311         msg = msg || this.invalidText;
16312         switch(this.msgTarget){
16313             case 'qtip':
16314                 obj.el.dom.qtip = msg;
16315                 obj.el.dom.qclass = 'x-form-invalid-tip';
16316                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
16317                     Roo.QuickTips.enable();
16318                 }
16319                 break;
16320             case 'title':
16321                 this.el.dom.title = msg;
16322                 break;
16323             case 'under':
16324                 if(!this.errorEl){
16325                     var elp = this.el.findParent('.x-form-element', 5, true);
16326                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
16327                     this.errorEl.setWidth(elp.getWidth(true)-20);
16328                 }
16329                 this.errorEl.update(msg);
16330                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
16331                 break;
16332             case 'side':
16333                 if(!this.errorIcon){
16334                     var elp = this.el.findParent('.x-form-element', 5, true);
16335                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
16336                 }
16337                 this.alignErrorIcon();
16338                 this.errorIcon.dom.qtip = msg;
16339                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
16340                 this.errorIcon.show();
16341                 this.on('resize', this.alignErrorIcon, this);
16342                 break;
16343             default:
16344                 var t = Roo.getDom(this.msgTarget);
16345                 t.innerHTML = msg;
16346                 t.style.display = this.msgDisplay;
16347                 break;
16348         }
16349         this.fireEvent('invalid', this, msg);
16350     },
16351
16352     // private
16353     alignErrorIcon : function(){
16354         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
16355     },
16356
16357     /**
16358      * Clear any invalid styles/messages for this field
16359      */
16360     clearInvalid : function(){
16361         if(!this.rendered || this.preventMark){ // not rendered
16362             return;
16363         }
16364         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
16365         
16366         obj.el.removeClass(this.invalidClass);
16367         switch(this.msgTarget){
16368             case 'qtip':
16369                 obj.el.dom.qtip = '';
16370                 break;
16371             case 'title':
16372                 this.el.dom.title = '';
16373                 break;
16374             case 'under':
16375                 if(this.errorEl){
16376                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
16377                 }
16378                 break;
16379             case 'side':
16380                 if(this.errorIcon){
16381                     this.errorIcon.dom.qtip = '';
16382                     this.errorIcon.hide();
16383                     this.un('resize', this.alignErrorIcon, this);
16384                 }
16385                 break;
16386             default:
16387                 var t = Roo.getDom(this.msgTarget);
16388                 t.innerHTML = '';
16389                 t.style.display = 'none';
16390                 break;
16391         }
16392         this.fireEvent('valid', this);
16393     },
16394
16395     /**
16396      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
16397      * @return {Mixed} value The field value
16398      */
16399     getRawValue : function(){
16400         var v = this.el.getValue();
16401         
16402         return v;
16403     },
16404
16405     /**
16406      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
16407      * @return {Mixed} value The field value
16408      */
16409     getValue : function(){
16410         var v = this.el.getValue();
16411          
16412         return v;
16413     },
16414
16415     /**
16416      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
16417      * @param {Mixed} value The value to set
16418      */
16419     setRawValue : function(v){
16420         return this.el.dom.value = (v === null || v === undefined ? '' : v);
16421     },
16422
16423     /**
16424      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
16425      * @param {Mixed} value The value to set
16426      */
16427     setValue : function(v){
16428         this.value = v;
16429         if(this.rendered){
16430             this.el.dom.value = (v === null || v === undefined ? '' : v);
16431              this.validate();
16432         }
16433     },
16434
16435     adjustSize : function(w, h){
16436         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
16437         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
16438         return s;
16439     },
16440
16441     adjustWidth : function(tag, w){
16442         tag = tag.toLowerCase();
16443         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
16444             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
16445                 if(tag == 'input'){
16446                     return w + 2;
16447                 }
16448                 if(tag == 'textarea'){
16449                     return w-2;
16450                 }
16451             }else if(Roo.isOpera){
16452                 if(tag == 'input'){
16453                     return w + 2;
16454                 }
16455                 if(tag == 'textarea'){
16456                     return w-2;
16457                 }
16458             }
16459         }
16460         return w;
16461     }
16462 });
16463
16464
16465 // anything other than normal should be considered experimental
16466 Roo.form.Field.msgFx = {
16467     normal : {
16468         show: function(msgEl, f){
16469             msgEl.setDisplayed('block');
16470         },
16471
16472         hide : function(msgEl, f){
16473             msgEl.setDisplayed(false).update('');
16474         }
16475     },
16476
16477     slide : {
16478         show: function(msgEl, f){
16479             msgEl.slideIn('t', {stopFx:true});
16480         },
16481
16482         hide : function(msgEl, f){
16483             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
16484         }
16485     },
16486
16487     slideRight : {
16488         show: function(msgEl, f){
16489             msgEl.fixDisplay();
16490             msgEl.alignTo(f.el, 'tl-tr');
16491             msgEl.slideIn('l', {stopFx:true});
16492         },
16493
16494         hide : function(msgEl, f){
16495             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
16496         }
16497     }
16498 };/*
16499  * Based on:
16500  * Ext JS Library 1.1.1
16501  * Copyright(c) 2006-2007, Ext JS, LLC.
16502  *
16503  * Originally Released Under LGPL - original licence link has changed is not relivant.
16504  *
16505  * Fork - LGPL
16506  * <script type="text/javascript">
16507  */
16508  
16509
16510 /**
16511  * @class Roo.form.TextField
16512  * @extends Roo.form.Field
16513  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
16514  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
16515  * @constructor
16516  * Creates a new TextField
16517  * @param {Object} config Configuration options
16518  */
16519 Roo.form.TextField = function(config){
16520     Roo.form.TextField.superclass.constructor.call(this, config);
16521     this.addEvents({
16522         /**
16523          * @event autosize
16524          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
16525          * according to the default logic, but this event provides a hook for the developer to apply additional
16526          * logic at runtime to resize the field if needed.
16527              * @param {Roo.form.Field} this This text field
16528              * @param {Number} width The new field width
16529              */
16530         autosize : true
16531     });
16532 };
16533
16534 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
16535     /**
16536      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
16537      */
16538     grow : false,
16539     /**
16540      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
16541      */
16542     growMin : 30,
16543     /**
16544      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
16545      */
16546     growMax : 800,
16547     /**
16548      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
16549      */
16550     vtype : null,
16551     /**
16552      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
16553      */
16554     maskRe : null,
16555     /**
16556      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
16557      */
16558     disableKeyFilter : false,
16559     /**
16560      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
16561      */
16562     allowBlank : true,
16563     /**
16564      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
16565      */
16566     minLength : 0,
16567     /**
16568      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
16569      */
16570     maxLength : Number.MAX_VALUE,
16571     /**
16572      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
16573      */
16574     minLengthText : "The minimum length for this field is {0}",
16575     /**
16576      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
16577      */
16578     maxLengthText : "The maximum length for this field is {0}",
16579     /**
16580      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
16581      */
16582     selectOnFocus : false,
16583     /**
16584      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
16585      */    
16586     allowLeadingSpace : false,
16587     /**
16588      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
16589      */
16590     blankText : "This field is required",
16591     /**
16592      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
16593      * If available, this function will be called only after the basic validators all return true, and will be passed the
16594      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
16595      */
16596     validator : null,
16597     /**
16598      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
16599      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
16600      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
16601      */
16602     regex : null,
16603     /**
16604      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
16605      */
16606     regexText : "",
16607     /**
16608      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
16609      */
16610     emptyText : null,
16611    
16612
16613     // private
16614     initEvents : function()
16615     {
16616         if (this.emptyText) {
16617             this.el.attr('placeholder', this.emptyText);
16618         }
16619         
16620         Roo.form.TextField.superclass.initEvents.call(this);
16621         if(this.validationEvent == 'keyup'){
16622             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
16623             this.el.on('keyup', this.filterValidation, this);
16624         }
16625         else if(this.validationEvent !== false){
16626             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
16627         }
16628         
16629         if(this.selectOnFocus){
16630             this.on("focus", this.preFocus, this);
16631         }
16632         if (!this.allowLeadingSpace) {
16633             this.on('blur', this.cleanLeadingSpace, this);
16634         }
16635         
16636         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
16637             this.el.on("keypress", this.filterKeys, this);
16638         }
16639         if(this.grow){
16640             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
16641             this.el.on("click", this.autoSize,  this);
16642         }
16643         if(this.el.is('input[type=password]') && Roo.isSafari){
16644             this.el.on('keydown', this.SafariOnKeyDown, this);
16645         }
16646     },
16647
16648     processValue : function(value){
16649         if(this.stripCharsRe){
16650             var newValue = value.replace(this.stripCharsRe, '');
16651             if(newValue !== value){
16652                 this.setRawValue(newValue);
16653                 return newValue;
16654             }
16655         }
16656         return value;
16657     },
16658
16659     filterValidation : function(e){
16660         if(!e.isNavKeyPress()){
16661             this.validationTask.delay(this.validationDelay);
16662         }
16663     },
16664
16665     // private
16666     onKeyUp : function(e){
16667         if(!e.isNavKeyPress()){
16668             this.autoSize();
16669         }
16670     },
16671     // private - clean the leading white space
16672     cleanLeadingSpace : function(e)
16673     {
16674         if ( this.inputType == 'file') {
16675             return;
16676         }
16677         
16678         this.setValue((this.getValue() + '').replace(/^\s+/,''));
16679     },
16680     /**
16681      * Resets the current field value to the originally-loaded value and clears any validation messages.
16682      *  
16683      */
16684     reset : function(){
16685         Roo.form.TextField.superclass.reset.call(this);
16686        
16687     }, 
16688     // private
16689     preFocus : function(){
16690         
16691         if(this.selectOnFocus){
16692             this.el.dom.select();
16693         }
16694     },
16695
16696     
16697     // private
16698     filterKeys : function(e){
16699         var k = e.getKey();
16700         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
16701             return;
16702         }
16703         var c = e.getCharCode(), cc = String.fromCharCode(c);
16704         if(Roo.isIE && (e.isSpecialKey() || !cc)){
16705             return;
16706         }
16707         if(!this.maskRe.test(cc)){
16708             e.stopEvent();
16709         }
16710     },
16711
16712     setValue : function(v){
16713         
16714         Roo.form.TextField.superclass.setValue.apply(this, arguments);
16715         
16716         this.autoSize();
16717     },
16718
16719     /**
16720      * Validates a value according to the field's validation rules and marks the field as invalid
16721      * if the validation fails
16722      * @param {Mixed} value The value to validate
16723      * @return {Boolean} True if the value is valid, else false
16724      */
16725     validateValue : function(value){
16726         if(value.length < 1)  { // if it's blank
16727              if(this.allowBlank){
16728                 this.clearInvalid();
16729                 return true;
16730              }else{
16731                 this.markInvalid(this.blankText);
16732                 return false;
16733              }
16734         }
16735         if(value.length < this.minLength){
16736             this.markInvalid(String.format(this.minLengthText, this.minLength));
16737             return false;
16738         }
16739         if(value.length > this.maxLength){
16740             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
16741             return false;
16742         }
16743         if(this.vtype){
16744             var vt = Roo.form.VTypes;
16745             if(!vt[this.vtype](value, this)){
16746                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
16747                 return false;
16748             }
16749         }
16750         if(typeof this.validator == "function"){
16751             var msg = this.validator(value);
16752             if(msg !== true){
16753                 this.markInvalid(msg);
16754                 return false;
16755             }
16756         }
16757         if(this.regex && !this.regex.test(value)){
16758             this.markInvalid(this.regexText);
16759             return false;
16760         }
16761         return true;
16762     },
16763
16764     /**
16765      * Selects text in this field
16766      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
16767      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
16768      */
16769     selectText : function(start, end){
16770         var v = this.getRawValue();
16771         if(v.length > 0){
16772             start = start === undefined ? 0 : start;
16773             end = end === undefined ? v.length : end;
16774             var d = this.el.dom;
16775             if(d.setSelectionRange){
16776                 d.setSelectionRange(start, end);
16777             }else if(d.createTextRange){
16778                 var range = d.createTextRange();
16779                 range.moveStart("character", start);
16780                 range.moveEnd("character", v.length-end);
16781                 range.select();
16782             }
16783         }
16784     },
16785
16786     /**
16787      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
16788      * This only takes effect if grow = true, and fires the autosize event.
16789      */
16790     autoSize : function(){
16791         if(!this.grow || !this.rendered){
16792             return;
16793         }
16794         if(!this.metrics){
16795             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
16796         }
16797         var el = this.el;
16798         var v = el.dom.value;
16799         var d = document.createElement('div');
16800         d.appendChild(document.createTextNode(v));
16801         v = d.innerHTML;
16802         d = null;
16803         v += "&#160;";
16804         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
16805         this.el.setWidth(w);
16806         this.fireEvent("autosize", this, w);
16807     },
16808     
16809     // private
16810     SafariOnKeyDown : function(event)
16811     {
16812         // this is a workaround for a password hang bug on chrome/ webkit.
16813         
16814         var isSelectAll = false;
16815         
16816         if(this.el.dom.selectionEnd > 0){
16817             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
16818         }
16819         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
16820             event.preventDefault();
16821             this.setValue('');
16822             return;
16823         }
16824         
16825         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
16826             
16827             event.preventDefault();
16828             // this is very hacky as keydown always get's upper case.
16829             
16830             var cc = String.fromCharCode(event.getCharCode());
16831             
16832             
16833             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
16834             
16835         }
16836         
16837         
16838     }
16839 });/*
16840  * Based on:
16841  * Ext JS Library 1.1.1
16842  * Copyright(c) 2006-2007, Ext JS, LLC.
16843  *
16844  * Originally Released Under LGPL - original licence link has changed is not relivant.
16845  *
16846  * Fork - LGPL
16847  * <script type="text/javascript">
16848  */
16849  
16850 /**
16851  * @class Roo.form.Hidden
16852  * @extends Roo.form.TextField
16853  * Simple Hidden element used on forms 
16854  * 
16855  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
16856  * 
16857  * @constructor
16858  * Creates a new Hidden form element.
16859  * @param {Object} config Configuration options
16860  */
16861
16862
16863
16864 // easy hidden field...
16865 Roo.form.Hidden = function(config){
16866     Roo.form.Hidden.superclass.constructor.call(this, config);
16867 };
16868   
16869 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
16870     fieldLabel:      '',
16871     inputType:      'hidden',
16872     width:          50,
16873     allowBlank:     true,
16874     labelSeparator: '',
16875     hidden:         true,
16876     itemCls :       'x-form-item-display-none'
16877
16878
16879 });
16880
16881
16882 /*
16883  * Based on:
16884  * Ext JS Library 1.1.1
16885  * Copyright(c) 2006-2007, Ext JS, LLC.
16886  *
16887  * Originally Released Under LGPL - original licence link has changed is not relivant.
16888  *
16889  * Fork - LGPL
16890  * <script type="text/javascript">
16891  */
16892  
16893 /**
16894  * @class Roo.form.TriggerField
16895  * @extends Roo.form.TextField
16896  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
16897  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
16898  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
16899  * for which you can provide a custom implementation.  For example:
16900  * <pre><code>
16901 var trigger = new Roo.form.TriggerField();
16902 trigger.onTriggerClick = myTriggerFn;
16903 trigger.applyTo('my-field');
16904 </code></pre>
16905  *
16906  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
16907  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
16908  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
16909  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
16910  * @constructor
16911  * Create a new TriggerField.
16912  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
16913  * to the base TextField)
16914  */
16915 Roo.form.TriggerField = function(config){
16916     this.mimicing = false;
16917     Roo.form.TriggerField.superclass.constructor.call(this, config);
16918 };
16919
16920 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
16921     /**
16922      * @cfg {String} triggerClass A CSS class to apply to the trigger
16923      */
16924     /**
16925      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
16926      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
16927      */
16928     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
16929     /**
16930      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
16931      */
16932     hideTrigger:false,
16933
16934     /** @cfg {Boolean} grow @hide */
16935     /** @cfg {Number} growMin @hide */
16936     /** @cfg {Number} growMax @hide */
16937
16938     /**
16939      * @hide 
16940      * @method
16941      */
16942     autoSize: Roo.emptyFn,
16943     // private
16944     monitorTab : true,
16945     // private
16946     deferHeight : true,
16947
16948     
16949     actionMode : 'wrap',
16950     // private
16951     onResize : function(w, h){
16952         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
16953         if(typeof w == 'number'){
16954             var x = w - this.trigger.getWidth();
16955             this.el.setWidth(this.adjustWidth('input', x));
16956             this.trigger.setStyle('left', x+'px');
16957         }
16958     },
16959
16960     // private
16961     adjustSize : Roo.BoxComponent.prototype.adjustSize,
16962
16963     // private
16964     getResizeEl : function(){
16965         return this.wrap;
16966     },
16967
16968     // private
16969     getPositionEl : function(){
16970         return this.wrap;
16971     },
16972
16973     // private
16974     alignErrorIcon : function(){
16975         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
16976     },
16977
16978     // private
16979     onRender : function(ct, position){
16980         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
16981         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
16982         this.trigger = this.wrap.createChild(this.triggerConfig ||
16983                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
16984         if(this.hideTrigger){
16985             this.trigger.setDisplayed(false);
16986         }
16987         this.initTrigger();
16988         if(!this.width){
16989             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
16990         }
16991     },
16992
16993     // private
16994     initTrigger : function(){
16995         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
16996         this.trigger.addClassOnOver('x-form-trigger-over');
16997         this.trigger.addClassOnClick('x-form-trigger-click');
16998     },
16999
17000     // private
17001     onDestroy : function(){
17002         if(this.trigger){
17003             this.trigger.removeAllListeners();
17004             this.trigger.remove();
17005         }
17006         if(this.wrap){
17007             this.wrap.remove();
17008         }
17009         Roo.form.TriggerField.superclass.onDestroy.call(this);
17010     },
17011
17012     // private
17013     onFocus : function(){
17014         Roo.form.TriggerField.superclass.onFocus.call(this);
17015         if(!this.mimicing){
17016             this.wrap.addClass('x-trigger-wrap-focus');
17017             this.mimicing = true;
17018             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
17019             if(this.monitorTab){
17020                 this.el.on("keydown", this.checkTab, this);
17021             }
17022         }
17023     },
17024
17025     // private
17026     checkTab : function(e){
17027         if(e.getKey() == e.TAB){
17028             this.triggerBlur();
17029         }
17030     },
17031
17032     // private
17033     onBlur : function(){
17034         // do nothing
17035     },
17036
17037     // private
17038     mimicBlur : function(e, t){
17039         if(!this.wrap.contains(t) && this.validateBlur()){
17040             this.triggerBlur();
17041         }
17042     },
17043
17044     // private
17045     triggerBlur : function(){
17046         this.mimicing = false;
17047         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
17048         if(this.monitorTab){
17049             this.el.un("keydown", this.checkTab, this);
17050         }
17051         this.wrap.removeClass('x-trigger-wrap-focus');
17052         Roo.form.TriggerField.superclass.onBlur.call(this);
17053     },
17054
17055     // private
17056     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
17057     validateBlur : function(e, t){
17058         return true;
17059     },
17060
17061     // private
17062     onDisable : function(){
17063         Roo.form.TriggerField.superclass.onDisable.call(this);
17064         if(this.wrap){
17065             this.wrap.addClass('x-item-disabled');
17066         }
17067     },
17068
17069     // private
17070     onEnable : function(){
17071         Roo.form.TriggerField.superclass.onEnable.call(this);
17072         if(this.wrap){
17073             this.wrap.removeClass('x-item-disabled');
17074         }
17075     },
17076
17077     // private
17078     onShow : function(){
17079         var ae = this.getActionEl();
17080         
17081         if(ae){
17082             ae.dom.style.display = '';
17083             ae.dom.style.visibility = 'visible';
17084         }
17085     },
17086
17087     // private
17088     
17089     onHide : function(){
17090         var ae = this.getActionEl();
17091         ae.dom.style.display = 'none';
17092     },
17093
17094     /**
17095      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
17096      * by an implementing function.
17097      * @method
17098      * @param {EventObject} e
17099      */
17100     onTriggerClick : Roo.emptyFn
17101 });
17102
17103 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
17104 // to be extended by an implementing class.  For an example of implementing this class, see the custom
17105 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
17106 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
17107     initComponent : function(){
17108         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
17109
17110         this.triggerConfig = {
17111             tag:'span', cls:'x-form-twin-triggers', cn:[
17112             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
17113             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
17114         ]};
17115     },
17116
17117     getTrigger : function(index){
17118         return this.triggers[index];
17119     },
17120
17121     initTrigger : function(){
17122         var ts = this.trigger.select('.x-form-trigger', true);
17123         this.wrap.setStyle('overflow', 'hidden');
17124         var triggerField = this;
17125         ts.each(function(t, all, index){
17126             t.hide = function(){
17127                 var w = triggerField.wrap.getWidth();
17128                 this.dom.style.display = 'none';
17129                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17130             };
17131             t.show = function(){
17132                 var w = triggerField.wrap.getWidth();
17133                 this.dom.style.display = '';
17134                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
17135             };
17136             var triggerIndex = 'Trigger'+(index+1);
17137
17138             if(this['hide'+triggerIndex]){
17139                 t.dom.style.display = 'none';
17140             }
17141             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
17142             t.addClassOnOver('x-form-trigger-over');
17143             t.addClassOnClick('x-form-trigger-click');
17144         }, this);
17145         this.triggers = ts.elements;
17146     },
17147
17148     onTrigger1Click : Roo.emptyFn,
17149     onTrigger2Click : Roo.emptyFn
17150 });/*
17151  * Based on:
17152  * Ext JS Library 1.1.1
17153  * Copyright(c) 2006-2007, Ext JS, LLC.
17154  *
17155  * Originally Released Under LGPL - original licence link has changed is not relivant.
17156  *
17157  * Fork - LGPL
17158  * <script type="text/javascript">
17159  */
17160  
17161 /**
17162  * @class Roo.form.TextArea
17163  * @extends Roo.form.TextField
17164  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
17165  * support for auto-sizing.
17166  * @constructor
17167  * Creates a new TextArea
17168  * @param {Object} config Configuration options
17169  */
17170 Roo.form.TextArea = function(config){
17171     Roo.form.TextArea.superclass.constructor.call(this, config);
17172     // these are provided exchanges for backwards compat
17173     // minHeight/maxHeight were replaced by growMin/growMax to be
17174     // compatible with TextField growing config values
17175     if(this.minHeight !== undefined){
17176         this.growMin = this.minHeight;
17177     }
17178     if(this.maxHeight !== undefined){
17179         this.growMax = this.maxHeight;
17180     }
17181 };
17182
17183 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
17184     /**
17185      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
17186      */
17187     growMin : 60,
17188     /**
17189      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
17190      */
17191     growMax: 1000,
17192     /**
17193      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
17194      * in the field (equivalent to setting overflow: hidden, defaults to false)
17195      */
17196     preventScrollbars: false,
17197     /**
17198      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
17199      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
17200      */
17201
17202     // private
17203     onRender : function(ct, position){
17204         if(!this.el){
17205             this.defaultAutoCreate = {
17206                 tag: "textarea",
17207                 style:"width:300px;height:60px;",
17208                 autocomplete: "new-password"
17209             };
17210         }
17211         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
17212         if(this.grow){
17213             this.textSizeEl = Roo.DomHelper.append(document.body, {
17214                 tag: "pre", cls: "x-form-grow-sizer"
17215             });
17216             if(this.preventScrollbars){
17217                 this.el.setStyle("overflow", "hidden");
17218             }
17219             this.el.setHeight(this.growMin);
17220         }
17221     },
17222
17223     onDestroy : function(){
17224         if(this.textSizeEl){
17225             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
17226         }
17227         Roo.form.TextArea.superclass.onDestroy.call(this);
17228     },
17229
17230     // private
17231     onKeyUp : function(e){
17232         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
17233             this.autoSize();
17234         }
17235     },
17236
17237     /**
17238      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
17239      * This only takes effect if grow = true, and fires the autosize event if the height changes.
17240      */
17241     autoSize : function(){
17242         if(!this.grow || !this.textSizeEl){
17243             return;
17244         }
17245         var el = this.el;
17246         var v = el.dom.value;
17247         var ts = this.textSizeEl;
17248
17249         ts.innerHTML = '';
17250         ts.appendChild(document.createTextNode(v));
17251         v = ts.innerHTML;
17252
17253         Roo.fly(ts).setWidth(this.el.getWidth());
17254         if(v.length < 1){
17255             v = "&#160;&#160;";
17256         }else{
17257             if(Roo.isIE){
17258                 v = v.replace(/\n/g, '<p>&#160;</p>');
17259             }
17260             v += "&#160;\n&#160;";
17261         }
17262         ts.innerHTML = v;
17263         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
17264         if(h != this.lastHeight){
17265             this.lastHeight = h;
17266             this.el.setHeight(h);
17267             this.fireEvent("autosize", this, h);
17268         }
17269     }
17270 });/*
17271  * Based on:
17272  * Ext JS Library 1.1.1
17273  * Copyright(c) 2006-2007, Ext JS, LLC.
17274  *
17275  * Originally Released Under LGPL - original licence link has changed is not relivant.
17276  *
17277  * Fork - LGPL
17278  * <script type="text/javascript">
17279  */
17280  
17281
17282 /**
17283  * @class Roo.form.NumberField
17284  * @extends Roo.form.TextField
17285  * Numeric text field that provides automatic keystroke filtering and numeric validation.
17286  * @constructor
17287  * Creates a new NumberField
17288  * @param {Object} config Configuration options
17289  */
17290 Roo.form.NumberField = function(config){
17291     Roo.form.NumberField.superclass.constructor.call(this, config);
17292 };
17293
17294 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
17295     /**
17296      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
17297      */
17298     fieldClass: "x-form-field x-form-num-field",
17299     /**
17300      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
17301      */
17302     allowDecimals : true,
17303     /**
17304      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
17305      */
17306     decimalSeparator : ".",
17307     /**
17308      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
17309      */
17310     decimalPrecision : 2,
17311     /**
17312      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
17313      */
17314     allowNegative : true,
17315     /**
17316      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
17317      */
17318     minValue : Number.NEGATIVE_INFINITY,
17319     /**
17320      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
17321      */
17322     maxValue : Number.MAX_VALUE,
17323     /**
17324      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
17325      */
17326     minText : "The minimum value for this field is {0}",
17327     /**
17328      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
17329      */
17330     maxText : "The maximum value for this field is {0}",
17331     /**
17332      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
17333      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
17334      */
17335     nanText : "{0} is not a valid number",
17336
17337     // private
17338     initEvents : function(){
17339         Roo.form.NumberField.superclass.initEvents.call(this);
17340         var allowed = "0123456789";
17341         if(this.allowDecimals){
17342             allowed += this.decimalSeparator;
17343         }
17344         if(this.allowNegative){
17345             allowed += "-";
17346         }
17347         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
17348         var keyPress = function(e){
17349             var k = e.getKey();
17350             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
17351                 return;
17352             }
17353             var c = e.getCharCode();
17354             if(allowed.indexOf(String.fromCharCode(c)) === -1){
17355                 e.stopEvent();
17356             }
17357         };
17358         this.el.on("keypress", keyPress, this);
17359     },
17360
17361     // private
17362     validateValue : function(value){
17363         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
17364             return false;
17365         }
17366         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17367              return true;
17368         }
17369         var num = this.parseValue(value);
17370         if(isNaN(num)){
17371             this.markInvalid(String.format(this.nanText, value));
17372             return false;
17373         }
17374         if(num < this.minValue){
17375             this.markInvalid(String.format(this.minText, this.minValue));
17376             return false;
17377         }
17378         if(num > this.maxValue){
17379             this.markInvalid(String.format(this.maxText, this.maxValue));
17380             return false;
17381         }
17382         return true;
17383     },
17384
17385     getValue : function(){
17386         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
17387     },
17388
17389     // private
17390     parseValue : function(value){
17391         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
17392         return isNaN(value) ? '' : value;
17393     },
17394
17395     // private
17396     fixPrecision : function(value){
17397         var nan = isNaN(value);
17398         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
17399             return nan ? '' : value;
17400         }
17401         return parseFloat(value).toFixed(this.decimalPrecision);
17402     },
17403
17404     setValue : function(v){
17405         v = this.fixPrecision(v);
17406         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
17407     },
17408
17409     // private
17410     decimalPrecisionFcn : function(v){
17411         return Math.floor(v);
17412     },
17413
17414     beforeBlur : function(){
17415         var v = this.parseValue(this.getRawValue());
17416         if(v){
17417             this.setValue(v);
17418         }
17419     }
17420 });/*
17421  * Based on:
17422  * Ext JS Library 1.1.1
17423  * Copyright(c) 2006-2007, Ext JS, LLC.
17424  *
17425  * Originally Released Under LGPL - original licence link has changed is not relivant.
17426  *
17427  * Fork - LGPL
17428  * <script type="text/javascript">
17429  */
17430  
17431 /**
17432  * @class Roo.form.DateField
17433  * @extends Roo.form.TriggerField
17434  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17435 * @constructor
17436 * Create a new DateField
17437 * @param {Object} config
17438  */
17439 Roo.form.DateField = function(config)
17440 {
17441     Roo.form.DateField.superclass.constructor.call(this, config);
17442     
17443       this.addEvents({
17444          
17445         /**
17446          * @event select
17447          * Fires when a date is selected
17448              * @param {Roo.form.DateField} combo This combo box
17449              * @param {Date} date The date selected
17450              */
17451         'select' : true
17452          
17453     });
17454     
17455     
17456     if(typeof this.minValue == "string") {
17457         this.minValue = this.parseDate(this.minValue);
17458     }
17459     if(typeof this.maxValue == "string") {
17460         this.maxValue = this.parseDate(this.maxValue);
17461     }
17462     this.ddMatch = null;
17463     if(this.disabledDates){
17464         var dd = this.disabledDates;
17465         var re = "(?:";
17466         for(var i = 0; i < dd.length; i++){
17467             re += dd[i];
17468             if(i != dd.length-1) {
17469                 re += "|";
17470             }
17471         }
17472         this.ddMatch = new RegExp(re + ")");
17473     }
17474 };
17475
17476 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
17477     /**
17478      * @cfg {String} format
17479      * The default date format string which can be overriden for localization support.  The format must be
17480      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17481      */
17482     format : "m/d/y",
17483     /**
17484      * @cfg {String} altFormats
17485      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17486      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17487      */
17488     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17489     /**
17490      * @cfg {Array} disabledDays
17491      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17492      */
17493     disabledDays : null,
17494     /**
17495      * @cfg {String} disabledDaysText
17496      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17497      */
17498     disabledDaysText : "Disabled",
17499     /**
17500      * @cfg {Array} disabledDates
17501      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17502      * expression so they are very powerful. Some examples:
17503      * <ul>
17504      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17505      * <li>["03/08", "09/16"] would disable those days for every year</li>
17506      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17507      * <li>["03/../2006"] would disable every day in March 2006</li>
17508      * <li>["^03"] would disable every day in every March</li>
17509      * </ul>
17510      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17511      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17512      */
17513     disabledDates : null,
17514     /**
17515      * @cfg {String} disabledDatesText
17516      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17517      */
17518     disabledDatesText : "Disabled",
17519     /**
17520      * @cfg {Date/String} minValue
17521      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17522      * valid format (defaults to null).
17523      */
17524     minValue : null,
17525     /**
17526      * @cfg {Date/String} maxValue
17527      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17528      * valid format (defaults to null).
17529      */
17530     maxValue : null,
17531     /**
17532      * @cfg {String} minText
17533      * The error text to display when the date in the cell is before minValue (defaults to
17534      * 'The date in this field must be after {minValue}').
17535      */
17536     minText : "The date in this field must be equal to or after {0}",
17537     /**
17538      * @cfg {String} maxText
17539      * The error text to display when the date in the cell is after maxValue (defaults to
17540      * 'The date in this field must be before {maxValue}').
17541      */
17542     maxText : "The date in this field must be equal to or before {0}",
17543     /**
17544      * @cfg {String} invalidText
17545      * The error text to display when the date in the field is invalid (defaults to
17546      * '{value} is not a valid date - it must be in the format {format}').
17547      */
17548     invalidText : "{0} is not a valid date - it must be in the format {1}",
17549     /**
17550      * @cfg {String} triggerClass
17551      * An additional CSS class used to style the trigger button.  The trigger will always get the
17552      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17553      * which displays a calendar icon).
17554      */
17555     triggerClass : 'x-form-date-trigger',
17556     
17557
17558     /**
17559      * @cfg {Boolean} useIso
17560      * if enabled, then the date field will use a hidden field to store the 
17561      * real value as iso formated date. default (false)
17562      */ 
17563     useIso : false,
17564     /**
17565      * @cfg {String/Object} autoCreate
17566      * A DomHelper element spec, or true for a default element spec (defaults to
17567      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17568      */ 
17569     // private
17570     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
17571     
17572     // private
17573     hiddenField: false,
17574     
17575     onRender : function(ct, position)
17576     {
17577         Roo.form.DateField.superclass.onRender.call(this, ct, position);
17578         if (this.useIso) {
17579             //this.el.dom.removeAttribute('name'); 
17580             Roo.log("Changing name?");
17581             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
17582             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17583                     'before', true);
17584             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17585             // prevent input submission
17586             this.hiddenName = this.name;
17587         }
17588             
17589             
17590     },
17591     
17592     // private
17593     validateValue : function(value)
17594     {
17595         value = this.formatDate(value);
17596         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
17597             Roo.log('super failed');
17598             return false;
17599         }
17600         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17601              return true;
17602         }
17603         var svalue = value;
17604         value = this.parseDate(value);
17605         if(!value){
17606             Roo.log('parse date failed' + svalue);
17607             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17608             return false;
17609         }
17610         var time = value.getTime();
17611         if(this.minValue && time < this.minValue.getTime()){
17612             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17613             return false;
17614         }
17615         if(this.maxValue && time > this.maxValue.getTime()){
17616             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17617             return false;
17618         }
17619         if(this.disabledDays){
17620             var day = value.getDay();
17621             for(var i = 0; i < this.disabledDays.length; i++) {
17622                 if(day === this.disabledDays[i]){
17623                     this.markInvalid(this.disabledDaysText);
17624                     return false;
17625                 }
17626             }
17627         }
17628         var fvalue = this.formatDate(value);
17629         if(this.ddMatch && this.ddMatch.test(fvalue)){
17630             this.markInvalid(String.format(this.disabledDatesText, fvalue));
17631             return false;
17632         }
17633         return true;
17634     },
17635
17636     // private
17637     // Provides logic to override the default TriggerField.validateBlur which just returns true
17638     validateBlur : function(){
17639         return !this.menu || !this.menu.isVisible();
17640     },
17641     
17642     getName: function()
17643     {
17644         // returns hidden if it's set..
17645         if (!this.rendered) {return ''};
17646         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
17647         
17648     },
17649
17650     /**
17651      * Returns the current date value of the date field.
17652      * @return {Date} The date value
17653      */
17654     getValue : function(){
17655         
17656         return  this.hiddenField ?
17657                 this.hiddenField.value :
17658                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
17659     },
17660
17661     /**
17662      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
17663      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
17664      * (the default format used is "m/d/y").
17665      * <br />Usage:
17666      * <pre><code>
17667 //All of these calls set the same date value (May 4, 2006)
17668
17669 //Pass a date object:
17670 var dt = new Date('5/4/06');
17671 dateField.setValue(dt);
17672
17673 //Pass a date string (default format):
17674 dateField.setValue('5/4/06');
17675
17676 //Pass a date string (custom format):
17677 dateField.format = 'Y-m-d';
17678 dateField.setValue('2006-5-4');
17679 </code></pre>
17680      * @param {String/Date} date The date or valid date string
17681      */
17682     setValue : function(date){
17683         if (this.hiddenField) {
17684             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
17685         }
17686         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
17687         // make sure the value field is always stored as a date..
17688         this.value = this.parseDate(date);
17689         
17690         
17691     },
17692
17693     // private
17694     parseDate : function(value){
17695         if(!value || value instanceof Date){
17696             return value;
17697         }
17698         var v = Date.parseDate(value, this.format);
17699          if (!v && this.useIso) {
17700             v = Date.parseDate(value, 'Y-m-d');
17701         }
17702         if(!v && this.altFormats){
17703             if(!this.altFormatsArray){
17704                 this.altFormatsArray = this.altFormats.split("|");
17705             }
17706             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17707                 v = Date.parseDate(value, this.altFormatsArray[i]);
17708             }
17709         }
17710         return v;
17711     },
17712
17713     // private
17714     formatDate : function(date, fmt){
17715         return (!date || !(date instanceof Date)) ?
17716                date : date.dateFormat(fmt || this.format);
17717     },
17718
17719     // private
17720     menuListeners : {
17721         select: function(m, d){
17722             
17723             this.setValue(d);
17724             this.fireEvent('select', this, d);
17725         },
17726         show : function(){ // retain focus styling
17727             this.onFocus();
17728         },
17729         hide : function(){
17730             this.focus.defer(10, this);
17731             var ml = this.menuListeners;
17732             this.menu.un("select", ml.select,  this);
17733             this.menu.un("show", ml.show,  this);
17734             this.menu.un("hide", ml.hide,  this);
17735         }
17736     },
17737
17738     // private
17739     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
17740     onTriggerClick : function(){
17741         if(this.disabled){
17742             return;
17743         }
17744         if(this.menu == null){
17745             this.menu = new Roo.menu.DateMenu();
17746         }
17747         Roo.apply(this.menu.picker,  {
17748             showClear: this.allowBlank,
17749             minDate : this.minValue,
17750             maxDate : this.maxValue,
17751             disabledDatesRE : this.ddMatch,
17752             disabledDatesText : this.disabledDatesText,
17753             disabledDays : this.disabledDays,
17754             disabledDaysText : this.disabledDaysText,
17755             format : this.useIso ? 'Y-m-d' : this.format,
17756             minText : String.format(this.minText, this.formatDate(this.minValue)),
17757             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
17758         });
17759         this.menu.on(Roo.apply({}, this.menuListeners, {
17760             scope:this
17761         }));
17762         this.menu.picker.setValue(this.getValue() || new Date());
17763         this.menu.show(this.el, "tl-bl?");
17764     },
17765
17766     beforeBlur : function(){
17767         var v = this.parseDate(this.getRawValue());
17768         if(v){
17769             this.setValue(v);
17770         }
17771     },
17772
17773     /*@
17774      * overide
17775      * 
17776      */
17777     isDirty : function() {
17778         if(this.disabled) {
17779             return false;
17780         }
17781         
17782         if(typeof(this.startValue) === 'undefined'){
17783             return false;
17784         }
17785         
17786         return String(this.getValue()) !== String(this.startValue);
17787         
17788     },
17789     // @overide
17790     cleanLeadingSpace : function(e)
17791     {
17792        return;
17793     }
17794     
17795 });/*
17796  * Based on:
17797  * Ext JS Library 1.1.1
17798  * Copyright(c) 2006-2007, Ext JS, LLC.
17799  *
17800  * Originally Released Under LGPL - original licence link has changed is not relivant.
17801  *
17802  * Fork - LGPL
17803  * <script type="text/javascript">
17804  */
17805  
17806 /**
17807  * @class Roo.form.MonthField
17808  * @extends Roo.form.TriggerField
17809  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
17810 * @constructor
17811 * Create a new MonthField
17812 * @param {Object} config
17813  */
17814 Roo.form.MonthField = function(config){
17815     
17816     Roo.form.MonthField.superclass.constructor.call(this, config);
17817     
17818       this.addEvents({
17819          
17820         /**
17821          * @event select
17822          * Fires when a date is selected
17823              * @param {Roo.form.MonthFieeld} combo This combo box
17824              * @param {Date} date The date selected
17825              */
17826         'select' : true
17827          
17828     });
17829     
17830     
17831     if(typeof this.minValue == "string") {
17832         this.minValue = this.parseDate(this.minValue);
17833     }
17834     if(typeof this.maxValue == "string") {
17835         this.maxValue = this.parseDate(this.maxValue);
17836     }
17837     this.ddMatch = null;
17838     if(this.disabledDates){
17839         var dd = this.disabledDates;
17840         var re = "(?:";
17841         for(var i = 0; i < dd.length; i++){
17842             re += dd[i];
17843             if(i != dd.length-1) {
17844                 re += "|";
17845             }
17846         }
17847         this.ddMatch = new RegExp(re + ")");
17848     }
17849 };
17850
17851 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
17852     /**
17853      * @cfg {String} format
17854      * The default date format string which can be overriden for localization support.  The format must be
17855      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17856      */
17857     format : "M Y",
17858     /**
17859      * @cfg {String} altFormats
17860      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17861      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17862      */
17863     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
17864     /**
17865      * @cfg {Array} disabledDays
17866      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
17867      */
17868     disabledDays : [0,1,2,3,4,5,6],
17869     /**
17870      * @cfg {String} disabledDaysText
17871      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
17872      */
17873     disabledDaysText : "Disabled",
17874     /**
17875      * @cfg {Array} disabledDates
17876      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
17877      * expression so they are very powerful. Some examples:
17878      * <ul>
17879      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
17880      * <li>["03/08", "09/16"] would disable those days for every year</li>
17881      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
17882      * <li>["03/../2006"] would disable every day in March 2006</li>
17883      * <li>["^03"] would disable every day in every March</li>
17884      * </ul>
17885      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
17886      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
17887      */
17888     disabledDates : null,
17889     /**
17890      * @cfg {String} disabledDatesText
17891      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
17892      */
17893     disabledDatesText : "Disabled",
17894     /**
17895      * @cfg {Date/String} minValue
17896      * The minimum allowed date. Can be either a Javascript date object or a string date in a
17897      * valid format (defaults to null).
17898      */
17899     minValue : null,
17900     /**
17901      * @cfg {Date/String} maxValue
17902      * The maximum allowed date. Can be either a Javascript date object or a string date in a
17903      * valid format (defaults to null).
17904      */
17905     maxValue : null,
17906     /**
17907      * @cfg {String} minText
17908      * The error text to display when the date in the cell is before minValue (defaults to
17909      * 'The date in this field must be after {minValue}').
17910      */
17911     minText : "The date in this field must be equal to or after {0}",
17912     /**
17913      * @cfg {String} maxTextf
17914      * The error text to display when the date in the cell is after maxValue (defaults to
17915      * 'The date in this field must be before {maxValue}').
17916      */
17917     maxText : "The date in this field must be equal to or before {0}",
17918     /**
17919      * @cfg {String} invalidText
17920      * The error text to display when the date in the field is invalid (defaults to
17921      * '{value} is not a valid date - it must be in the format {format}').
17922      */
17923     invalidText : "{0} is not a valid date - it must be in the format {1}",
17924     /**
17925      * @cfg {String} triggerClass
17926      * An additional CSS class used to style the trigger button.  The trigger will always get the
17927      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
17928      * which displays a calendar icon).
17929      */
17930     triggerClass : 'x-form-date-trigger',
17931     
17932
17933     /**
17934      * @cfg {Boolean} useIso
17935      * if enabled, then the date field will use a hidden field to store the 
17936      * real value as iso formated date. default (true)
17937      */ 
17938     useIso : true,
17939     /**
17940      * @cfg {String/Object} autoCreate
17941      * A DomHelper element spec, or true for a default element spec (defaults to
17942      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
17943      */ 
17944     // private
17945     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
17946     
17947     // private
17948     hiddenField: false,
17949     
17950     hideMonthPicker : false,
17951     
17952     onRender : function(ct, position)
17953     {
17954         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
17955         if (this.useIso) {
17956             this.el.dom.removeAttribute('name'); 
17957             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
17958                     'before', true);
17959             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
17960             // prevent input submission
17961             this.hiddenName = this.name;
17962         }
17963             
17964             
17965     },
17966     
17967     // private
17968     validateValue : function(value)
17969     {
17970         value = this.formatDate(value);
17971         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
17972             return false;
17973         }
17974         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
17975              return true;
17976         }
17977         var svalue = value;
17978         value = this.parseDate(value);
17979         if(!value){
17980             this.markInvalid(String.format(this.invalidText, svalue, this.format));
17981             return false;
17982         }
17983         var time = value.getTime();
17984         if(this.minValue && time < this.minValue.getTime()){
17985             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
17986             return false;
17987         }
17988         if(this.maxValue && time > this.maxValue.getTime()){
17989             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
17990             return false;
17991         }
17992         /*if(this.disabledDays){
17993             var day = value.getDay();
17994             for(var i = 0; i < this.disabledDays.length; i++) {
17995                 if(day === this.disabledDays[i]){
17996                     this.markInvalid(this.disabledDaysText);
17997                     return false;
17998                 }
17999             }
18000         }
18001         */
18002         var fvalue = this.formatDate(value);
18003         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
18004             this.markInvalid(String.format(this.disabledDatesText, fvalue));
18005             return false;
18006         }
18007         */
18008         return true;
18009     },
18010
18011     // private
18012     // Provides logic to override the default TriggerField.validateBlur which just returns true
18013     validateBlur : function(){
18014         return !this.menu || !this.menu.isVisible();
18015     },
18016
18017     /**
18018      * Returns the current date value of the date field.
18019      * @return {Date} The date value
18020      */
18021     getValue : function(){
18022         
18023         
18024         
18025         return  this.hiddenField ?
18026                 this.hiddenField.value :
18027                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
18028     },
18029
18030     /**
18031      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
18032      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
18033      * (the default format used is "m/d/y").
18034      * <br />Usage:
18035      * <pre><code>
18036 //All of these calls set the same date value (May 4, 2006)
18037
18038 //Pass a date object:
18039 var dt = new Date('5/4/06');
18040 monthField.setValue(dt);
18041
18042 //Pass a date string (default format):
18043 monthField.setValue('5/4/06');
18044
18045 //Pass a date string (custom format):
18046 monthField.format = 'Y-m-d';
18047 monthField.setValue('2006-5-4');
18048 </code></pre>
18049      * @param {String/Date} date The date or valid date string
18050      */
18051     setValue : function(date){
18052         Roo.log('month setValue' + date);
18053         // can only be first of month..
18054         
18055         var val = this.parseDate(date);
18056         
18057         if (this.hiddenField) {
18058             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
18059         }
18060         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
18061         this.value = this.parseDate(date);
18062     },
18063
18064     // private
18065     parseDate : function(value){
18066         if(!value || value instanceof Date){
18067             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
18068             return value;
18069         }
18070         var v = Date.parseDate(value, this.format);
18071         if (!v && this.useIso) {
18072             v = Date.parseDate(value, 'Y-m-d');
18073         }
18074         if (v) {
18075             // 
18076             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
18077         }
18078         
18079         
18080         if(!v && this.altFormats){
18081             if(!this.altFormatsArray){
18082                 this.altFormatsArray = this.altFormats.split("|");
18083             }
18084             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18085                 v = Date.parseDate(value, this.altFormatsArray[i]);
18086             }
18087         }
18088         return v;
18089     },
18090
18091     // private
18092     formatDate : function(date, fmt){
18093         return (!date || !(date instanceof Date)) ?
18094                date : date.dateFormat(fmt || this.format);
18095     },
18096
18097     // private
18098     menuListeners : {
18099         select: function(m, d){
18100             this.setValue(d);
18101             this.fireEvent('select', this, d);
18102         },
18103         show : function(){ // retain focus styling
18104             this.onFocus();
18105         },
18106         hide : function(){
18107             this.focus.defer(10, this);
18108             var ml = this.menuListeners;
18109             this.menu.un("select", ml.select,  this);
18110             this.menu.un("show", ml.show,  this);
18111             this.menu.un("hide", ml.hide,  this);
18112         }
18113     },
18114     // private
18115     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
18116     onTriggerClick : function(){
18117         if(this.disabled){
18118             return;
18119         }
18120         if(this.menu == null){
18121             this.menu = new Roo.menu.DateMenu();
18122            
18123         }
18124         
18125         Roo.apply(this.menu.picker,  {
18126             
18127             showClear: this.allowBlank,
18128             minDate : this.minValue,
18129             maxDate : this.maxValue,
18130             disabledDatesRE : this.ddMatch,
18131             disabledDatesText : this.disabledDatesText,
18132             
18133             format : this.useIso ? 'Y-m-d' : this.format,
18134             minText : String.format(this.minText, this.formatDate(this.minValue)),
18135             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
18136             
18137         });
18138          this.menu.on(Roo.apply({}, this.menuListeners, {
18139             scope:this
18140         }));
18141        
18142         
18143         var m = this.menu;
18144         var p = m.picker;
18145         
18146         // hide month picker get's called when we called by 'before hide';
18147         
18148         var ignorehide = true;
18149         p.hideMonthPicker  = function(disableAnim){
18150             if (ignorehide) {
18151                 return;
18152             }
18153              if(this.monthPicker){
18154                 Roo.log("hideMonthPicker called");
18155                 if(disableAnim === true){
18156                     this.monthPicker.hide();
18157                 }else{
18158                     this.monthPicker.slideOut('t', {duration:.2});
18159                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
18160                     p.fireEvent("select", this, this.value);
18161                     m.hide();
18162                 }
18163             }
18164         }
18165         
18166         Roo.log('picker set value');
18167         Roo.log(this.getValue());
18168         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
18169         m.show(this.el, 'tl-bl?');
18170         ignorehide  = false;
18171         // this will trigger hideMonthPicker..
18172         
18173         
18174         // hidden the day picker
18175         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
18176         
18177         
18178         
18179       
18180         
18181         p.showMonthPicker.defer(100, p);
18182     
18183         
18184        
18185     },
18186
18187     beforeBlur : function(){
18188         var v = this.parseDate(this.getRawValue());
18189         if(v){
18190             this.setValue(v);
18191         }
18192     }
18193
18194     /** @cfg {Boolean} grow @hide */
18195     /** @cfg {Number} growMin @hide */
18196     /** @cfg {Number} growMax @hide */
18197     /**
18198      * @hide
18199      * @method autoSize
18200      */
18201 });/*
18202  * Based on:
18203  * Ext JS Library 1.1.1
18204  * Copyright(c) 2006-2007, Ext JS, LLC.
18205  *
18206  * Originally Released Under LGPL - original licence link has changed is not relivant.
18207  *
18208  * Fork - LGPL
18209  * <script type="text/javascript">
18210  */
18211  
18212
18213 /**
18214  * @class Roo.form.ComboBox
18215  * @extends Roo.form.TriggerField
18216  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
18217  * @constructor
18218  * Create a new ComboBox.
18219  * @param {Object} config Configuration options
18220  */
18221 Roo.form.ComboBox = function(config){
18222     Roo.form.ComboBox.superclass.constructor.call(this, config);
18223     this.addEvents({
18224         /**
18225          * @event expand
18226          * Fires when the dropdown list is expanded
18227              * @param {Roo.form.ComboBox} combo This combo box
18228              */
18229         'expand' : true,
18230         /**
18231          * @event collapse
18232          * Fires when the dropdown list is collapsed
18233              * @param {Roo.form.ComboBox} combo This combo box
18234              */
18235         'collapse' : true,
18236         /**
18237          * @event beforeselect
18238          * Fires before a list item is selected. Return false to cancel the selection.
18239              * @param {Roo.form.ComboBox} combo This combo box
18240              * @param {Roo.data.Record} record The data record returned from the underlying store
18241              * @param {Number} index The index of the selected item in the dropdown list
18242              */
18243         'beforeselect' : true,
18244         /**
18245          * @event select
18246          * Fires when a list item is selected
18247              * @param {Roo.form.ComboBox} combo This combo box
18248              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
18249              * @param {Number} index The index of the selected item in the dropdown list
18250              */
18251         'select' : true,
18252         /**
18253          * @event beforequery
18254          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
18255          * The event object passed has these properties:
18256              * @param {Roo.form.ComboBox} combo This combo box
18257              * @param {String} query The query
18258              * @param {Boolean} forceAll true to force "all" query
18259              * @param {Boolean} cancel true to cancel the query
18260              * @param {Object} e The query event object
18261              */
18262         'beforequery': true,
18263          /**
18264          * @event add
18265          * Fires when the 'add' icon is pressed (add a listener to enable add button)
18266              * @param {Roo.form.ComboBox} combo This combo box
18267              */
18268         'add' : true,
18269         /**
18270          * @event edit
18271          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
18272              * @param {Roo.form.ComboBox} combo This combo box
18273              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
18274              */
18275         'edit' : true
18276         
18277         
18278     });
18279     if(this.transform){
18280         this.allowDomMove = false;
18281         var s = Roo.getDom(this.transform);
18282         if(!this.hiddenName){
18283             this.hiddenName = s.name;
18284         }
18285         if(!this.store){
18286             this.mode = 'local';
18287             var d = [], opts = s.options;
18288             for(var i = 0, len = opts.length;i < len; i++){
18289                 var o = opts[i];
18290                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
18291                 if(o.selected) {
18292                     this.value = value;
18293                 }
18294                 d.push([value, o.text]);
18295             }
18296             this.store = new Roo.data.SimpleStore({
18297                 'id': 0,
18298                 fields: ['value', 'text'],
18299                 data : d
18300             });
18301             this.valueField = 'value';
18302             this.displayField = 'text';
18303         }
18304         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
18305         if(!this.lazyRender){
18306             this.target = true;
18307             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
18308             s.parentNode.removeChild(s); // remove it
18309             this.render(this.el.parentNode);
18310         }else{
18311             s.parentNode.removeChild(s); // remove it
18312         }
18313
18314     }
18315     if (this.store) {
18316         this.store = Roo.factory(this.store, Roo.data);
18317     }
18318     
18319     this.selectedIndex = -1;
18320     if(this.mode == 'local'){
18321         if(config.queryDelay === undefined){
18322             this.queryDelay = 10;
18323         }
18324         if(config.minChars === undefined){
18325             this.minChars = 0;
18326         }
18327     }
18328 };
18329
18330 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
18331     /**
18332      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
18333      */
18334     /**
18335      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
18336      * rendering into an Roo.Editor, defaults to false)
18337      */
18338     /**
18339      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
18340      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
18341      */
18342     /**
18343      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
18344      */
18345     /**
18346      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
18347      * the dropdown list (defaults to undefined, with no header element)
18348      */
18349
18350      /**
18351      * @cfg {String/Roo.Template} tpl The template to use to render the output
18352      */
18353      
18354     // private
18355     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
18356     /**
18357      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
18358      */
18359     listWidth: undefined,
18360     /**
18361      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
18362      * mode = 'remote' or 'text' if mode = 'local')
18363      */
18364     displayField: undefined,
18365     /**
18366      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
18367      * mode = 'remote' or 'value' if mode = 'local'). 
18368      * Note: use of a valueField requires the user make a selection
18369      * in order for a value to be mapped.
18370      */
18371     valueField: undefined,
18372     
18373     
18374     /**
18375      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
18376      * field's data value (defaults to the underlying DOM element's name)
18377      */
18378     hiddenName: undefined,
18379     /**
18380      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
18381      */
18382     listClass: '',
18383     /**
18384      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
18385      */
18386     selectedClass: 'x-combo-selected',
18387     /**
18388      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
18389      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
18390      * which displays a downward arrow icon).
18391      */
18392     triggerClass : 'x-form-arrow-trigger',
18393     /**
18394      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
18395      */
18396     shadow:'sides',
18397     /**
18398      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
18399      * anchor positions (defaults to 'tl-bl')
18400      */
18401     listAlign: 'tl-bl?',
18402     /**
18403      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
18404      */
18405     maxHeight: 300,
18406     /**
18407      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
18408      * query specified by the allQuery config option (defaults to 'query')
18409      */
18410     triggerAction: 'query',
18411     /**
18412      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
18413      * (defaults to 4, does not apply if editable = false)
18414      */
18415     minChars : 4,
18416     /**
18417      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
18418      * delay (typeAheadDelay) if it matches a known value (defaults to false)
18419      */
18420     typeAhead: false,
18421     /**
18422      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
18423      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
18424      */
18425     queryDelay: 500,
18426     /**
18427      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
18428      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
18429      */
18430     pageSize: 0,
18431     /**
18432      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
18433      * when editable = true (defaults to false)
18434      */
18435     selectOnFocus:false,
18436     /**
18437      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
18438      */
18439     queryParam: 'query',
18440     /**
18441      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
18442      * when mode = 'remote' (defaults to 'Loading...')
18443      */
18444     loadingText: 'Loading...',
18445     /**
18446      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
18447      */
18448     resizable: false,
18449     /**
18450      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
18451      */
18452     handleHeight : 8,
18453     /**
18454      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
18455      * traditional select (defaults to true)
18456      */
18457     editable: true,
18458     /**
18459      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
18460      */
18461     allQuery: '',
18462     /**
18463      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
18464      */
18465     mode: 'remote',
18466     /**
18467      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
18468      * listWidth has a higher value)
18469      */
18470     minListWidth : 70,
18471     /**
18472      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
18473      * allow the user to set arbitrary text into the field (defaults to false)
18474      */
18475     forceSelection:false,
18476     /**
18477      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
18478      * if typeAhead = true (defaults to 250)
18479      */
18480     typeAheadDelay : 250,
18481     /**
18482      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
18483      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
18484      */
18485     valueNotFoundText : undefined,
18486     /**
18487      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
18488      */
18489     blockFocus : false,
18490     
18491     /**
18492      * @cfg {Boolean} disableClear Disable showing of clear button.
18493      */
18494     disableClear : false,
18495     /**
18496      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
18497      */
18498     alwaysQuery : false,
18499     
18500     //private
18501     addicon : false,
18502     editicon: false,
18503     
18504     // element that contains real text value.. (when hidden is used..)
18505      
18506     // private
18507     onRender : function(ct, position)
18508     {
18509         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
18510         
18511         if(this.hiddenName){
18512             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
18513                     'before', true);
18514             this.hiddenField.value =
18515                 this.hiddenValue !== undefined ? this.hiddenValue :
18516                 this.value !== undefined ? this.value : '';
18517
18518             // prevent input submission
18519             this.el.dom.removeAttribute('name');
18520              
18521              
18522         }
18523         
18524         if(Roo.isGecko){
18525             this.el.dom.setAttribute('autocomplete', 'off');
18526         }
18527
18528         var cls = 'x-combo-list';
18529
18530         this.list = new Roo.Layer({
18531             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
18532         });
18533
18534         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
18535         this.list.setWidth(lw);
18536         this.list.swallowEvent('mousewheel');
18537         this.assetHeight = 0;
18538
18539         if(this.title){
18540             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
18541             this.assetHeight += this.header.getHeight();
18542         }
18543
18544         this.innerList = this.list.createChild({cls:cls+'-inner'});
18545         this.innerList.on('mouseover', this.onViewOver, this);
18546         this.innerList.on('mousemove', this.onViewMove, this);
18547         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18548         
18549         if(this.allowBlank && !this.pageSize && !this.disableClear){
18550             this.footer = this.list.createChild({cls:cls+'-ft'});
18551             this.pageTb = new Roo.Toolbar(this.footer);
18552            
18553         }
18554         if(this.pageSize){
18555             this.footer = this.list.createChild({cls:cls+'-ft'});
18556             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
18557                     {pageSize: this.pageSize});
18558             
18559         }
18560         
18561         if (this.pageTb && this.allowBlank && !this.disableClear) {
18562             var _this = this;
18563             this.pageTb.add(new Roo.Toolbar.Fill(), {
18564                 cls: 'x-btn-icon x-btn-clear',
18565                 text: '&#160;',
18566                 handler: function()
18567                 {
18568                     _this.collapse();
18569                     _this.clearValue();
18570                     _this.onSelect(false, -1);
18571                 }
18572             });
18573         }
18574         if (this.footer) {
18575             this.assetHeight += this.footer.getHeight();
18576         }
18577         
18578
18579         if(!this.tpl){
18580             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
18581         }
18582
18583         this.view = new Roo.View(this.innerList, this.tpl, {
18584             singleSelect:true,
18585             store: this.store,
18586             selectedClass: this.selectedClass
18587         });
18588
18589         this.view.on('click', this.onViewClick, this);
18590
18591         this.store.on('beforeload', this.onBeforeLoad, this);
18592         this.store.on('load', this.onLoad, this);
18593         this.store.on('loadexception', this.onLoadException, this);
18594
18595         if(this.resizable){
18596             this.resizer = new Roo.Resizable(this.list,  {
18597                pinned:true, handles:'se'
18598             });
18599             this.resizer.on('resize', function(r, w, h){
18600                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
18601                 this.listWidth = w;
18602                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
18603                 this.restrictHeight();
18604             }, this);
18605             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
18606         }
18607         if(!this.editable){
18608             this.editable = true;
18609             this.setEditable(false);
18610         }  
18611         
18612         
18613         if (typeof(this.events.add.listeners) != 'undefined') {
18614             
18615             this.addicon = this.wrap.createChild(
18616                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
18617        
18618             this.addicon.on('click', function(e) {
18619                 this.fireEvent('add', this);
18620             }, this);
18621         }
18622         if (typeof(this.events.edit.listeners) != 'undefined') {
18623             
18624             this.editicon = this.wrap.createChild(
18625                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
18626             if (this.addicon) {
18627                 this.editicon.setStyle('margin-left', '40px');
18628             }
18629             this.editicon.on('click', function(e) {
18630                 
18631                 // we fire even  if inothing is selected..
18632                 this.fireEvent('edit', this, this.lastData );
18633                 
18634             }, this);
18635         }
18636         
18637         
18638         
18639     },
18640
18641     // private
18642     initEvents : function(){
18643         Roo.form.ComboBox.superclass.initEvents.call(this);
18644
18645         this.keyNav = new Roo.KeyNav(this.el, {
18646             "up" : function(e){
18647                 this.inKeyMode = true;
18648                 this.selectPrev();
18649             },
18650
18651             "down" : function(e){
18652                 if(!this.isExpanded()){
18653                     this.onTriggerClick();
18654                 }else{
18655                     this.inKeyMode = true;
18656                     this.selectNext();
18657                 }
18658             },
18659
18660             "enter" : function(e){
18661                 this.onViewClick();
18662                 //return true;
18663             },
18664
18665             "esc" : function(e){
18666                 this.collapse();
18667             },
18668
18669             "tab" : function(e){
18670                 this.onViewClick(false);
18671                 this.fireEvent("specialkey", this, e);
18672                 return true;
18673             },
18674
18675             scope : this,
18676
18677             doRelay : function(foo, bar, hname){
18678                 if(hname == 'down' || this.scope.isExpanded()){
18679                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
18680                 }
18681                 return true;
18682             },
18683
18684             forceKeyDown: true
18685         });
18686         this.queryDelay = Math.max(this.queryDelay || 10,
18687                 this.mode == 'local' ? 10 : 250);
18688         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
18689         if(this.typeAhead){
18690             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
18691         }
18692         if(this.editable !== false){
18693             this.el.on("keyup", this.onKeyUp, this);
18694         }
18695         if(this.forceSelection){
18696             this.on('blur', this.doForce, this);
18697         }
18698     },
18699
18700     onDestroy : function(){
18701         if(this.view){
18702             this.view.setStore(null);
18703             this.view.el.removeAllListeners();
18704             this.view.el.remove();
18705             this.view.purgeListeners();
18706         }
18707         if(this.list){
18708             this.list.destroy();
18709         }
18710         if(this.store){
18711             this.store.un('beforeload', this.onBeforeLoad, this);
18712             this.store.un('load', this.onLoad, this);
18713             this.store.un('loadexception', this.onLoadException, this);
18714         }
18715         Roo.form.ComboBox.superclass.onDestroy.call(this);
18716     },
18717
18718     // private
18719     fireKey : function(e){
18720         if(e.isNavKeyPress() && !this.list.isVisible()){
18721             this.fireEvent("specialkey", this, e);
18722         }
18723     },
18724
18725     // private
18726     onResize: function(w, h){
18727         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
18728         
18729         if(typeof w != 'number'){
18730             // we do not handle it!?!?
18731             return;
18732         }
18733         var tw = this.trigger.getWidth();
18734         tw += this.addicon ? this.addicon.getWidth() : 0;
18735         tw += this.editicon ? this.editicon.getWidth() : 0;
18736         var x = w - tw;
18737         this.el.setWidth( this.adjustWidth('input', x));
18738             
18739         this.trigger.setStyle('left', x+'px');
18740         
18741         if(this.list && this.listWidth === undefined){
18742             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
18743             this.list.setWidth(lw);
18744             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
18745         }
18746         
18747     
18748         
18749     },
18750
18751     /**
18752      * Allow or prevent the user from directly editing the field text.  If false is passed,
18753      * the user will only be able to select from the items defined in the dropdown list.  This method
18754      * is the runtime equivalent of setting the 'editable' config option at config time.
18755      * @param {Boolean} value True to allow the user to directly edit the field text
18756      */
18757     setEditable : function(value){
18758         if(value == this.editable){
18759             return;
18760         }
18761         this.editable = value;
18762         if(!value){
18763             this.el.dom.setAttribute('readOnly', true);
18764             this.el.on('mousedown', this.onTriggerClick,  this);
18765             this.el.addClass('x-combo-noedit');
18766         }else{
18767             this.el.dom.setAttribute('readOnly', false);
18768             this.el.un('mousedown', this.onTriggerClick,  this);
18769             this.el.removeClass('x-combo-noedit');
18770         }
18771     },
18772
18773     // private
18774     onBeforeLoad : function(){
18775         if(!this.hasFocus){
18776             return;
18777         }
18778         this.innerList.update(this.loadingText ?
18779                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
18780         this.restrictHeight();
18781         this.selectedIndex = -1;
18782     },
18783
18784     // private
18785     onLoad : function(){
18786         if(!this.hasFocus){
18787             return;
18788         }
18789         if(this.store.getCount() > 0){
18790             this.expand();
18791             this.restrictHeight();
18792             if(this.lastQuery == this.allQuery){
18793                 if(this.editable){
18794                     this.el.dom.select();
18795                 }
18796                 if(!this.selectByValue(this.value, true)){
18797                     this.select(0, true);
18798                 }
18799             }else{
18800                 this.selectNext();
18801                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18802                     this.taTask.delay(this.typeAheadDelay);
18803                 }
18804             }
18805         }else{
18806             this.onEmptyResults();
18807         }
18808         //this.el.focus();
18809     },
18810     // private
18811     onLoadException : function()
18812     {
18813         this.collapse();
18814         Roo.log(this.store.reader.jsonData);
18815         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18816             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18817         }
18818         
18819         
18820     },
18821     // private
18822     onTypeAhead : function(){
18823         if(this.store.getCount() > 0){
18824             var r = this.store.getAt(0);
18825             var newValue = r.data[this.displayField];
18826             var len = newValue.length;
18827             var selStart = this.getRawValue().length;
18828             if(selStart != len){
18829                 this.setRawValue(newValue);
18830                 this.selectText(selStart, newValue.length);
18831             }
18832         }
18833     },
18834
18835     // private
18836     onSelect : function(record, index){
18837         if(this.fireEvent('beforeselect', this, record, index) !== false){
18838             this.setFromData(index > -1 ? record.data : false);
18839             this.collapse();
18840             this.fireEvent('select', this, record, index);
18841         }
18842     },
18843
18844     /**
18845      * Returns the currently selected field value or empty string if no value is set.
18846      * @return {String} value The selected value
18847      */
18848     getValue : function(){
18849         if(this.valueField){
18850             return typeof this.value != 'undefined' ? this.value : '';
18851         }
18852         return Roo.form.ComboBox.superclass.getValue.call(this);
18853     },
18854
18855     /**
18856      * Clears any text/value currently set in the field
18857      */
18858     clearValue : function(){
18859         if(this.hiddenField){
18860             this.hiddenField.value = '';
18861         }
18862         this.value = '';
18863         this.setRawValue('');
18864         this.lastSelectionText = '';
18865         
18866     },
18867
18868     /**
18869      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
18870      * will be displayed in the field.  If the value does not match the data value of an existing item,
18871      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18872      * Otherwise the field will be blank (although the value will still be set).
18873      * @param {String} value The value to match
18874      */
18875     setValue : function(v){
18876         var text = v;
18877         if(this.valueField){
18878             var r = this.findRecord(this.valueField, v);
18879             if(r){
18880                 text = r.data[this.displayField];
18881             }else if(this.valueNotFoundText !== undefined){
18882                 text = this.valueNotFoundText;
18883             }
18884         }
18885         this.lastSelectionText = text;
18886         if(this.hiddenField){
18887             this.hiddenField.value = v;
18888         }
18889         Roo.form.ComboBox.superclass.setValue.call(this, text);
18890         this.value = v;
18891     },
18892     /**
18893      * @property {Object} the last set data for the element
18894      */
18895     
18896     lastData : false,
18897     /**
18898      * Sets the value of the field based on a object which is related to the record format for the store.
18899      * @param {Object} value the value to set as. or false on reset?
18900      */
18901     setFromData : function(o){
18902         var dv = ''; // display value
18903         var vv = ''; // value value..
18904         this.lastData = o;
18905         if (this.displayField) {
18906             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18907         } else {
18908             // this is an error condition!!!
18909             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18910         }
18911         
18912         if(this.valueField){
18913             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18914         }
18915         if(this.hiddenField){
18916             this.hiddenField.value = vv;
18917             
18918             this.lastSelectionText = dv;
18919             Roo.form.ComboBox.superclass.setValue.call(this, dv);
18920             this.value = vv;
18921             return;
18922         }
18923         // no hidden field.. - we store the value in 'value', but still display
18924         // display field!!!!
18925         this.lastSelectionText = dv;
18926         Roo.form.ComboBox.superclass.setValue.call(this, dv);
18927         this.value = vv;
18928         
18929         
18930     },
18931     // private
18932     reset : function(){
18933         // overridden so that last data is reset..
18934         this.setValue(this.resetValue);
18935         this.originalValue = this.getValue();
18936         this.clearInvalid();
18937         this.lastData = false;
18938         if (this.view) {
18939             this.view.clearSelections();
18940         }
18941     },
18942     // private
18943     findRecord : function(prop, value){
18944         var record;
18945         if(this.store.getCount() > 0){
18946             this.store.each(function(r){
18947                 if(r.data[prop] == value){
18948                     record = r;
18949                     return false;
18950                 }
18951                 return true;
18952             });
18953         }
18954         return record;
18955     },
18956     
18957     getName: function()
18958     {
18959         // returns hidden if it's set..
18960         if (!this.rendered) {return ''};
18961         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
18962         
18963     },
18964     // private
18965     onViewMove : function(e, t){
18966         this.inKeyMode = false;
18967     },
18968
18969     // private
18970     onViewOver : function(e, t){
18971         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18972             return;
18973         }
18974         var item = this.view.findItemFromChild(t);
18975         if(item){
18976             var index = this.view.indexOf(item);
18977             this.select(index, false);
18978         }
18979     },
18980
18981     // private
18982     onViewClick : function(doFocus)
18983     {
18984         var index = this.view.getSelectedIndexes()[0];
18985         var r = this.store.getAt(index);
18986         if(r){
18987             this.onSelect(r, index);
18988         }
18989         if(doFocus !== false && !this.blockFocus){
18990             this.el.focus();
18991         }
18992     },
18993
18994     // private
18995     restrictHeight : function(){
18996         this.innerList.dom.style.height = '';
18997         var inner = this.innerList.dom;
18998         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18999         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19000         this.list.beginUpdate();
19001         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
19002         this.list.alignTo(this.el, this.listAlign);
19003         this.list.endUpdate();
19004     },
19005
19006     // private
19007     onEmptyResults : function(){
19008         this.collapse();
19009     },
19010
19011     /**
19012      * Returns true if the dropdown list is expanded, else false.
19013      */
19014     isExpanded : function(){
19015         return this.list.isVisible();
19016     },
19017
19018     /**
19019      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
19020      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19021      * @param {String} value The data value of the item to select
19022      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19023      * selected item if it is not currently in view (defaults to true)
19024      * @return {Boolean} True if the value matched an item in the list, else false
19025      */
19026     selectByValue : function(v, scrollIntoView){
19027         if(v !== undefined && v !== null){
19028             var r = this.findRecord(this.valueField || this.displayField, v);
19029             if(r){
19030                 this.select(this.store.indexOf(r), scrollIntoView);
19031                 return true;
19032             }
19033         }
19034         return false;
19035     },
19036
19037     /**
19038      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
19039      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
19040      * @param {Number} index The zero-based index of the list item to select
19041      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
19042      * selected item if it is not currently in view (defaults to true)
19043      */
19044     select : function(index, scrollIntoView){
19045         this.selectedIndex = index;
19046         this.view.select(index);
19047         if(scrollIntoView !== false){
19048             var el = this.view.getNode(index);
19049             if(el){
19050                 this.innerList.scrollChildIntoView(el, false);
19051             }
19052         }
19053     },
19054
19055     // private
19056     selectNext : function(){
19057         var ct = this.store.getCount();
19058         if(ct > 0){
19059             if(this.selectedIndex == -1){
19060                 this.select(0);
19061             }else if(this.selectedIndex < ct-1){
19062                 this.select(this.selectedIndex+1);
19063             }
19064         }
19065     },
19066
19067     // private
19068     selectPrev : function(){
19069         var ct = this.store.getCount();
19070         if(ct > 0){
19071             if(this.selectedIndex == -1){
19072                 this.select(0);
19073             }else if(this.selectedIndex != 0){
19074                 this.select(this.selectedIndex-1);
19075             }
19076         }
19077     },
19078
19079     // private
19080     onKeyUp : function(e){
19081         if(this.editable !== false && !e.isSpecialKey()){
19082             this.lastKey = e.getKey();
19083             this.dqTask.delay(this.queryDelay);
19084         }
19085     },
19086
19087     // private
19088     validateBlur : function(){
19089         return !this.list || !this.list.isVisible();   
19090     },
19091
19092     // private
19093     initQuery : function(){
19094         this.doQuery(this.getRawValue());
19095     },
19096
19097     // private
19098     doForce : function(){
19099         if(this.el.dom.value.length > 0){
19100             this.el.dom.value =
19101                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
19102              
19103         }
19104     },
19105
19106     /**
19107      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
19108      * query allowing the query action to be canceled if needed.
19109      * @param {String} query The SQL query to execute
19110      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
19111      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
19112      * saved in the current store (defaults to false)
19113      */
19114     doQuery : function(q, forceAll){
19115         if(q === undefined || q === null){
19116             q = '';
19117         }
19118         var qe = {
19119             query: q,
19120             forceAll: forceAll,
19121             combo: this,
19122             cancel:false
19123         };
19124         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
19125             return false;
19126         }
19127         q = qe.query;
19128         forceAll = qe.forceAll;
19129         if(forceAll === true || (q.length >= this.minChars)){
19130             if(this.lastQuery != q || this.alwaysQuery){
19131                 this.lastQuery = q;
19132                 if(this.mode == 'local'){
19133                     this.selectedIndex = -1;
19134                     if(forceAll){
19135                         this.store.clearFilter();
19136                     }else{
19137                         this.store.filter(this.displayField, q);
19138                     }
19139                     this.onLoad();
19140                 }else{
19141                     this.store.baseParams[this.queryParam] = q;
19142                     this.store.load({
19143                         params: this.getParams(q)
19144                     });
19145                     this.expand();
19146                 }
19147             }else{
19148                 this.selectedIndex = -1;
19149                 this.onLoad();   
19150             }
19151         }
19152     },
19153
19154     // private
19155     getParams : function(q){
19156         var p = {};
19157         //p[this.queryParam] = q;
19158         if(this.pageSize){
19159             p.start = 0;
19160             p.limit = this.pageSize;
19161         }
19162         return p;
19163     },
19164
19165     /**
19166      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
19167      */
19168     collapse : function(){
19169         if(!this.isExpanded()){
19170             return;
19171         }
19172         this.list.hide();
19173         Roo.get(document).un('mousedown', this.collapseIf, this);
19174         Roo.get(document).un('mousewheel', this.collapseIf, this);
19175         if (!this.editable) {
19176             Roo.get(document).un('keydown', this.listKeyPress, this);
19177         }
19178         this.fireEvent('collapse', this);
19179     },
19180
19181     // private
19182     collapseIf : function(e){
19183         if(!e.within(this.wrap) && !e.within(this.list)){
19184             this.collapse();
19185         }
19186     },
19187
19188     /**
19189      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
19190      */
19191     expand : function(){
19192         if(this.isExpanded() || !this.hasFocus){
19193             return;
19194         }
19195         this.list.alignTo(this.el, this.listAlign);
19196         this.list.show();
19197         Roo.get(document).on('mousedown', this.collapseIf, this);
19198         Roo.get(document).on('mousewheel', this.collapseIf, this);
19199         if (!this.editable) {
19200             Roo.get(document).on('keydown', this.listKeyPress, this);
19201         }
19202         
19203         this.fireEvent('expand', this);
19204     },
19205
19206     // private
19207     // Implements the default empty TriggerField.onTriggerClick function
19208     onTriggerClick : function(){
19209         if(this.disabled){
19210             return;
19211         }
19212         if(this.isExpanded()){
19213             this.collapse();
19214             if (!this.blockFocus) {
19215                 this.el.focus();
19216             }
19217             
19218         }else {
19219             this.hasFocus = true;
19220             if(this.triggerAction == 'all') {
19221                 this.doQuery(this.allQuery, true);
19222             } else {
19223                 this.doQuery(this.getRawValue());
19224             }
19225             if (!this.blockFocus) {
19226                 this.el.focus();
19227             }
19228         }
19229     },
19230     listKeyPress : function(e)
19231     {
19232         //Roo.log('listkeypress');
19233         // scroll to first matching element based on key pres..
19234         if (e.isSpecialKey()) {
19235             return false;
19236         }
19237         var k = String.fromCharCode(e.getKey()).toUpperCase();
19238         //Roo.log(k);
19239         var match  = false;
19240         var csel = this.view.getSelectedNodes();
19241         var cselitem = false;
19242         if (csel.length) {
19243             var ix = this.view.indexOf(csel[0]);
19244             cselitem  = this.store.getAt(ix);
19245             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
19246                 cselitem = false;
19247             }
19248             
19249         }
19250         
19251         this.store.each(function(v) { 
19252             if (cselitem) {
19253                 // start at existing selection.
19254                 if (cselitem.id == v.id) {
19255                     cselitem = false;
19256                 }
19257                 return;
19258             }
19259                 
19260             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
19261                 match = this.store.indexOf(v);
19262                 return false;
19263             }
19264         }, this);
19265         
19266         if (match === false) {
19267             return true; // no more action?
19268         }
19269         // scroll to?
19270         this.view.select(match);
19271         var sn = Roo.get(this.view.getSelectedNodes()[0]);
19272         sn.scrollIntoView(sn.dom.parentNode, false);
19273     } 
19274
19275     /** 
19276     * @cfg {Boolean} grow 
19277     * @hide 
19278     */
19279     /** 
19280     * @cfg {Number} growMin 
19281     * @hide 
19282     */
19283     /** 
19284     * @cfg {Number} growMax 
19285     * @hide 
19286     */
19287     /**
19288      * @hide
19289      * @method autoSize
19290      */
19291 });/*
19292  * Copyright(c) 2010-2012, Roo J Solutions Limited
19293  *
19294  * Licence LGPL
19295  *
19296  */
19297
19298 /**
19299  * @class Roo.form.ComboBoxArray
19300  * @extends Roo.form.TextField
19301  * A facebook style adder... for lists of email / people / countries  etc...
19302  * pick multiple items from a combo box, and shows each one.
19303  *
19304  *  Fred [x]  Brian [x]  [Pick another |v]
19305  *
19306  *
19307  *  For this to work: it needs various extra information
19308  *    - normal combo problay has
19309  *      name, hiddenName
19310  *    + displayField, valueField
19311  *
19312  *    For our purpose...
19313  *
19314  *
19315  *   If we change from 'extends' to wrapping...
19316  *   
19317  *  
19318  *
19319  
19320  
19321  * @constructor
19322  * Create a new ComboBoxArray.
19323  * @param {Object} config Configuration options
19324  */
19325  
19326
19327 Roo.form.ComboBoxArray = function(config)
19328 {
19329     this.addEvents({
19330         /**
19331          * @event beforeremove
19332          * Fires before remove the value from the list
19333              * @param {Roo.form.ComboBoxArray} _self This combo box array
19334              * @param {Roo.form.ComboBoxArray.Item} item removed item
19335              */
19336         'beforeremove' : true,
19337         /**
19338          * @event remove
19339          * Fires when remove the value from the list
19340              * @param {Roo.form.ComboBoxArray} _self This combo box array
19341              * @param {Roo.form.ComboBoxArray.Item} item removed item
19342              */
19343         'remove' : true
19344         
19345         
19346     });
19347     
19348     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
19349     
19350     this.items = new Roo.util.MixedCollection(false);
19351     
19352     // construct the child combo...
19353     
19354     
19355     
19356     
19357    
19358     
19359 }
19360
19361  
19362 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
19363
19364     /**
19365      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
19366      */
19367     
19368     lastData : false,
19369     
19370     // behavies liek a hiddne field
19371     inputType:      'hidden',
19372     /**
19373      * @cfg {Number} width The width of the box that displays the selected element
19374      */ 
19375     width:          300,
19376
19377     
19378     
19379     /**
19380      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
19381      */
19382     name : false,
19383     /**
19384      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
19385      */
19386     hiddenName : false,
19387       /**
19388      * @cfg {String} seperator    The value seperator normally ',' 
19389      */
19390     seperator : ',',
19391     
19392     // private the array of items that are displayed..
19393     items  : false,
19394     // private - the hidden field el.
19395     hiddenEl : false,
19396     // private - the filed el..
19397     el : false,
19398     
19399     //validateValue : function() { return true; }, // all values are ok!
19400     //onAddClick: function() { },
19401     
19402     onRender : function(ct, position) 
19403     {
19404         
19405         // create the standard hidden element
19406         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
19407         
19408         
19409         // give fake names to child combo;
19410         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
19411         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
19412         
19413         this.combo = Roo.factory(this.combo, Roo.form);
19414         this.combo.onRender(ct, position);
19415         if (typeof(this.combo.width) != 'undefined') {
19416             this.combo.onResize(this.combo.width,0);
19417         }
19418         
19419         this.combo.initEvents();
19420         
19421         // assigned so form know we need to do this..
19422         this.store          = this.combo.store;
19423         this.valueField     = this.combo.valueField;
19424         this.displayField   = this.combo.displayField ;
19425         
19426         
19427         this.combo.wrap.addClass('x-cbarray-grp');
19428         
19429         var cbwrap = this.combo.wrap.createChild(
19430             {tag: 'div', cls: 'x-cbarray-cb'},
19431             this.combo.el.dom
19432         );
19433         
19434              
19435         this.hiddenEl = this.combo.wrap.createChild({
19436             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
19437         });
19438         this.el = this.combo.wrap.createChild({
19439             tag: 'input',  type:'hidden' , name: this.name, value : ''
19440         });
19441          //   this.el.dom.removeAttribute("name");
19442         
19443         
19444         this.outerWrap = this.combo.wrap;
19445         this.wrap = cbwrap;
19446         
19447         this.outerWrap.setWidth(this.width);
19448         this.outerWrap.dom.removeChild(this.el.dom);
19449         
19450         this.wrap.dom.appendChild(this.el.dom);
19451         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
19452         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
19453         
19454         this.combo.trigger.setStyle('position','relative');
19455         this.combo.trigger.setStyle('left', '0px');
19456         this.combo.trigger.setStyle('top', '2px');
19457         
19458         this.combo.el.setStyle('vertical-align', 'text-bottom');
19459         
19460         //this.trigger.setStyle('vertical-align', 'top');
19461         
19462         // this should use the code from combo really... on('add' ....)
19463         if (this.adder) {
19464             
19465         
19466             this.adder = this.outerWrap.createChild(
19467                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
19468             var _t = this;
19469             this.adder.on('click', function(e) {
19470                 _t.fireEvent('adderclick', this, e);
19471             }, _t);
19472         }
19473         //var _t = this;
19474         //this.adder.on('click', this.onAddClick, _t);
19475         
19476         
19477         this.combo.on('select', function(cb, rec, ix) {
19478             this.addItem(rec.data);
19479             
19480             cb.setValue('');
19481             cb.el.dom.value = '';
19482             //cb.lastData = rec.data;
19483             // add to list
19484             
19485         }, this);
19486         
19487         
19488     },
19489     
19490     
19491     getName: function()
19492     {
19493         // returns hidden if it's set..
19494         if (!this.rendered) {return ''};
19495         return  this.hiddenName ? this.hiddenName : this.name;
19496         
19497     },
19498     
19499     
19500     onResize: function(w, h){
19501         
19502         return;
19503         // not sure if this is needed..
19504         //this.combo.onResize(w,h);
19505         
19506         if(typeof w != 'number'){
19507             // we do not handle it!?!?
19508             return;
19509         }
19510         var tw = this.combo.trigger.getWidth();
19511         tw += this.addicon ? this.addicon.getWidth() : 0;
19512         tw += this.editicon ? this.editicon.getWidth() : 0;
19513         var x = w - tw;
19514         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
19515             
19516         this.combo.trigger.setStyle('left', '0px');
19517         
19518         if(this.list && this.listWidth === undefined){
19519             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
19520             this.list.setWidth(lw);
19521             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
19522         }
19523         
19524     
19525         
19526     },
19527     
19528     addItem: function(rec)
19529     {
19530         var valueField = this.combo.valueField;
19531         var displayField = this.combo.displayField;
19532         
19533         if (this.items.indexOfKey(rec[valueField]) > -1) {
19534             //console.log("GOT " + rec.data.id);
19535             return;
19536         }
19537         
19538         var x = new Roo.form.ComboBoxArray.Item({
19539             //id : rec[this.idField],
19540             data : rec,
19541             displayField : displayField ,
19542             tipField : displayField ,
19543             cb : this
19544         });
19545         // use the 
19546         this.items.add(rec[valueField],x);
19547         // add it before the element..
19548         this.updateHiddenEl();
19549         x.render(this.outerWrap, this.wrap.dom);
19550         // add the image handler..
19551     },
19552     
19553     updateHiddenEl : function()
19554     {
19555         this.validate();
19556         if (!this.hiddenEl) {
19557             return;
19558         }
19559         var ar = [];
19560         var idField = this.combo.valueField;
19561         
19562         this.items.each(function(f) {
19563             ar.push(f.data[idField]);
19564         });
19565         this.hiddenEl.dom.value = ar.join(this.seperator);
19566         this.validate();
19567     },
19568     
19569     reset : function()
19570     {
19571         this.items.clear();
19572         
19573         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
19574            el.remove();
19575         });
19576         
19577         this.el.dom.value = '';
19578         if (this.hiddenEl) {
19579             this.hiddenEl.dom.value = '';
19580         }
19581         
19582     },
19583     getValue: function()
19584     {
19585         return this.hiddenEl ? this.hiddenEl.dom.value : '';
19586     },
19587     setValue: function(v) // not a valid action - must use addItems..
19588     {
19589         
19590         this.reset();
19591          
19592         if (this.store.isLocal && (typeof(v) == 'string')) {
19593             // then we can use the store to find the values..
19594             // comma seperated at present.. this needs to allow JSON based encoding..
19595             this.hiddenEl.value  = v;
19596             var v_ar = [];
19597             Roo.each(v.split(this.seperator), function(k) {
19598                 Roo.log("CHECK " + this.valueField + ',' + k);
19599                 var li = this.store.query(this.valueField, k);
19600                 if (!li.length) {
19601                     return;
19602                 }
19603                 var add = {};
19604                 add[this.valueField] = k;
19605                 add[this.displayField] = li.item(0).data[this.displayField];
19606                 
19607                 this.addItem(add);
19608             }, this) 
19609              
19610         }
19611         if (typeof(v) == 'object' ) {
19612             // then let's assume it's an array of objects..
19613             Roo.each(v, function(l) {
19614                 var add = l;
19615                 if (typeof(l) == 'string') {
19616                     add = {};
19617                     add[this.valueField] = l;
19618                     add[this.displayField] = l
19619                 }
19620                 this.addItem(add);
19621             }, this);
19622              
19623         }
19624         
19625         
19626     },
19627     setFromData: function(v)
19628     {
19629         // this recieves an object, if setValues is called.
19630         this.reset();
19631         this.el.dom.value = v[this.displayField];
19632         this.hiddenEl.dom.value = v[this.valueField];
19633         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
19634             return;
19635         }
19636         var kv = v[this.valueField];
19637         var dv = v[this.displayField];
19638         kv = typeof(kv) != 'string' ? '' : kv;
19639         dv = typeof(dv) != 'string' ? '' : dv;
19640         
19641         
19642         var keys = kv.split(this.seperator);
19643         var display = dv.split(this.seperator);
19644         for (var i = 0 ; i < keys.length; i++) {
19645             add = {};
19646             add[this.valueField] = keys[i];
19647             add[this.displayField] = display[i];
19648             this.addItem(add);
19649         }
19650       
19651         
19652     },
19653     
19654     /**
19655      * Validates the combox array value
19656      * @return {Boolean} True if the value is valid, else false
19657      */
19658     validate : function(){
19659         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
19660             this.clearInvalid();
19661             return true;
19662         }
19663         return false;
19664     },
19665     
19666     validateValue : function(value){
19667         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
19668         
19669     },
19670     
19671     /*@
19672      * overide
19673      * 
19674      */
19675     isDirty : function() {
19676         if(this.disabled) {
19677             return false;
19678         }
19679         
19680         try {
19681             var d = Roo.decode(String(this.originalValue));
19682         } catch (e) {
19683             return String(this.getValue()) !== String(this.originalValue);
19684         }
19685         
19686         var originalValue = [];
19687         
19688         for (var i = 0; i < d.length; i++){
19689             originalValue.push(d[i][this.valueField]);
19690         }
19691         
19692         return String(this.getValue()) !== String(originalValue.join(this.seperator));
19693         
19694     }
19695     
19696 });
19697
19698
19699
19700 /**
19701  * @class Roo.form.ComboBoxArray.Item
19702  * @extends Roo.BoxComponent
19703  * A selected item in the list
19704  *  Fred [x]  Brian [x]  [Pick another |v]
19705  * 
19706  * @constructor
19707  * Create a new item.
19708  * @param {Object} config Configuration options
19709  */
19710  
19711 Roo.form.ComboBoxArray.Item = function(config) {
19712     config.id = Roo.id();
19713     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
19714 }
19715
19716 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
19717     data : {},
19718     cb: false,
19719     displayField : false,
19720     tipField : false,
19721     
19722     
19723     defaultAutoCreate : {
19724         tag: 'div',
19725         cls: 'x-cbarray-item',
19726         cn : [ 
19727             { tag: 'div' },
19728             {
19729                 tag: 'img',
19730                 width:16,
19731                 height : 16,
19732                 src : Roo.BLANK_IMAGE_URL ,
19733                 align: 'center'
19734             }
19735         ]
19736         
19737     },
19738     
19739  
19740     onRender : function(ct, position)
19741     {
19742         Roo.form.Field.superclass.onRender.call(this, ct, position);
19743         
19744         if(!this.el){
19745             var cfg = this.getAutoCreate();
19746             this.el = ct.createChild(cfg, position);
19747         }
19748         
19749         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
19750         
19751         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
19752             this.cb.renderer(this.data) :
19753             String.format('{0}',this.data[this.displayField]);
19754         
19755             
19756         this.el.child('div').dom.setAttribute('qtip',
19757                         String.format('{0}',this.data[this.tipField])
19758         );
19759         
19760         this.el.child('img').on('click', this.remove, this);
19761         
19762     },
19763    
19764     remove : function()
19765     {
19766         if(this.cb.disabled){
19767             return;
19768         }
19769         
19770         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
19771             this.cb.items.remove(this);
19772             this.el.child('img').un('click', this.remove, this);
19773             this.el.remove();
19774             this.cb.updateHiddenEl();
19775
19776             this.cb.fireEvent('remove', this.cb, this);
19777         }
19778         
19779     }
19780 });/*
19781  * RooJS Library 1.1.1
19782  * Copyright(c) 2008-2011  Alan Knowles
19783  *
19784  * License - LGPL
19785  */
19786  
19787
19788 /**
19789  * @class Roo.form.ComboNested
19790  * @extends Roo.form.ComboBox
19791  * A combobox for that allows selection of nested items in a list,
19792  * eg.
19793  *
19794  *  Book
19795  *    -> red
19796  *    -> green
19797  *  Table
19798  *    -> square
19799  *      ->red
19800  *      ->green
19801  *    -> rectangle
19802  *      ->green
19803  *      
19804  * 
19805  * @constructor
19806  * Create a new ComboNested
19807  * @param {Object} config Configuration options
19808  */
19809 Roo.form.ComboNested = function(config){
19810     Roo.form.ComboCheck.superclass.constructor.call(this, config);
19811     // should verify some data...
19812     // like
19813     // hiddenName = required..
19814     // displayField = required
19815     // valudField == required
19816     var req= [ 'hiddenName', 'displayField', 'valueField' ];
19817     var _t = this;
19818     Roo.each(req, function(e) {
19819         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
19820             throw "Roo.form.ComboNested : missing value for: " + e;
19821         }
19822     });
19823      
19824     
19825 };
19826
19827 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
19828    
19829     /*
19830      * @config {Number} max Number of columns to show
19831      */
19832     
19833     maxColumns : 3,
19834    
19835     list : null, // the outermost div..
19836     innerLists : null, // the
19837     views : null,
19838     stores : null,
19839     // private
19840     loadingChildren : false,
19841     
19842     onRender : function(ct, position)
19843     {
19844         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
19845         
19846         if(this.hiddenName){
19847             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
19848                     'before', true);
19849             this.hiddenField.value =
19850                 this.hiddenValue !== undefined ? this.hiddenValue :
19851                 this.value !== undefined ? this.value : '';
19852
19853             // prevent input submission
19854             this.el.dom.removeAttribute('name');
19855              
19856              
19857         }
19858         
19859         if(Roo.isGecko){
19860             this.el.dom.setAttribute('autocomplete', 'off');
19861         }
19862
19863         var cls = 'x-combo-list';
19864
19865         this.list = new Roo.Layer({
19866             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
19867         });
19868
19869         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
19870         this.list.setWidth(lw);
19871         this.list.swallowEvent('mousewheel');
19872         this.assetHeight = 0;
19873
19874         if(this.title){
19875             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
19876             this.assetHeight += this.header.getHeight();
19877         }
19878         this.innerLists = [];
19879         this.views = [];
19880         this.stores = [];
19881         for (var i =0 ; i < this.maxColumns; i++) {
19882             this.onRenderList( cls, i);
19883         }
19884         
19885         // always needs footer, as we are going to have an 'OK' button.
19886         this.footer = this.list.createChild({cls:cls+'-ft'});
19887         this.pageTb = new Roo.Toolbar(this.footer);  
19888         var _this = this;
19889         this.pageTb.add(  {
19890             
19891             text: 'Done',
19892             handler: function()
19893             {
19894                 _this.collapse();
19895             }
19896         });
19897         
19898         if ( this.allowBlank && !this.disableClear) {
19899             
19900             this.pageTb.add(new Roo.Toolbar.Fill(), {
19901                 cls: 'x-btn-icon x-btn-clear',
19902                 text: '&#160;',
19903                 handler: function()
19904                 {
19905                     _this.collapse();
19906                     _this.clearValue();
19907                     _this.onSelect(false, -1);
19908                 }
19909             });
19910         }
19911         if (this.footer) {
19912             this.assetHeight += this.footer.getHeight();
19913         }
19914         
19915     },
19916     onRenderList : function (  cls, i)
19917     {
19918         
19919         var lw = Math.floor(
19920                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
19921         );
19922         
19923         this.list.setWidth(lw); // default to '1'
19924
19925         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
19926         //il.on('mouseover', this.onViewOver, this, { list:  i });
19927         //il.on('mousemove', this.onViewMove, this, { list:  i });
19928         il.setWidth(lw);
19929         il.setStyle({ 'overflow-x' : 'hidden'});
19930
19931         if(!this.tpl){
19932             this.tpl = new Roo.Template({
19933                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
19934                 isEmpty: function (value, allValues) {
19935                     //Roo.log(value);
19936                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
19937                     return dl ? 'has-children' : 'no-children'
19938                 }
19939             });
19940         }
19941         
19942         var store  = this.store;
19943         if (i > 0) {
19944             store  = new Roo.data.SimpleStore({
19945                 //fields : this.store.reader.meta.fields,
19946                 reader : this.store.reader,
19947                 data : [ ]
19948             });
19949         }
19950         this.stores[i]  = store;
19951                   
19952         var view = this.views[i] = new Roo.View(
19953             il,
19954             this.tpl,
19955             {
19956                 singleSelect:true,
19957                 store: store,
19958                 selectedClass: this.selectedClass
19959             }
19960         );
19961         view.getEl().setWidth(lw);
19962         view.getEl().setStyle({
19963             position: i < 1 ? 'relative' : 'absolute',
19964             top: 0,
19965             left: (i * lw ) + 'px',
19966             display : i > 0 ? 'none' : 'block'
19967         });
19968         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
19969         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
19970         //view.on('click', this.onViewClick, this, { list : i });
19971
19972         store.on('beforeload', this.onBeforeLoad, this);
19973         store.on('load',  this.onLoad, this, { list  : i});
19974         store.on('loadexception', this.onLoadException, this);
19975
19976         // hide the other vies..
19977         
19978         
19979         
19980     },
19981       
19982     restrictHeight : function()
19983     {
19984         var mh = 0;
19985         Roo.each(this.innerLists, function(il,i) {
19986             var el = this.views[i].getEl();
19987             el.dom.style.height = '';
19988             var inner = el.dom;
19989             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
19990             // only adjust heights on other ones..
19991             mh = Math.max(h, mh);
19992             if (i < 1) {
19993                 
19994                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19995                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
19996                
19997             }
19998             
19999             
20000         }, this);
20001         
20002         this.list.beginUpdate();
20003         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
20004         this.list.alignTo(this.el, this.listAlign);
20005         this.list.endUpdate();
20006         
20007     },
20008      
20009     
20010     // -- store handlers..
20011     // private
20012     onBeforeLoad : function()
20013     {
20014         if(!this.hasFocus){
20015             return;
20016         }
20017         this.innerLists[0].update(this.loadingText ?
20018                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
20019         this.restrictHeight();
20020         this.selectedIndex = -1;
20021     },
20022     // private
20023     onLoad : function(a,b,c,d)
20024     {
20025         if (!this.loadingChildren) {
20026             // then we are loading the top level. - hide the children
20027             for (var i = 1;i < this.views.length; i++) {
20028                 this.views[i].getEl().setStyle({ display : 'none' });
20029             }
20030             var lw = Math.floor(
20031                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
20032             );
20033         
20034              this.list.setWidth(lw); // default to '1'
20035
20036             
20037         }
20038         if(!this.hasFocus){
20039             return;
20040         }
20041         
20042         if(this.store.getCount() > 0) {
20043             this.expand();
20044             this.restrictHeight();   
20045         } else {
20046             this.onEmptyResults();
20047         }
20048         
20049         if (!this.loadingChildren) {
20050             this.selectActive();
20051         }
20052         /*
20053         this.stores[1].loadData([]);
20054         this.stores[2].loadData([]);
20055         this.views
20056         */    
20057     
20058         //this.el.focus();
20059     },
20060     
20061     
20062     // private
20063     onLoadException : function()
20064     {
20065         this.collapse();
20066         Roo.log(this.store.reader.jsonData);
20067         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
20068             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
20069         }
20070         
20071         
20072     },
20073     // no cleaning of leading spaces on blur here.
20074     cleanLeadingSpace : function(e) { },
20075     
20076
20077     onSelectChange : function (view, sels, opts )
20078     {
20079         var ix = view.getSelectedIndexes();
20080          
20081         if (opts.list > this.maxColumns - 2) {
20082             if (view.store.getCount()<  1) {
20083                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
20084
20085             } else  {
20086                 if (ix.length) {
20087                     // used to clear ?? but if we are loading unselected 
20088                     this.setFromData(view.store.getAt(ix[0]).data);
20089                 }
20090                 
20091             }
20092             
20093             return;
20094         }
20095         
20096         if (!ix.length) {
20097             // this get's fired when trigger opens..
20098            // this.setFromData({});
20099             var str = this.stores[opts.list+1];
20100             str.data.clear(); // removeall wihtout the fire events..
20101             return;
20102         }
20103         
20104         var rec = view.store.getAt(ix[0]);
20105          
20106         this.setFromData(rec.data);
20107         this.fireEvent('select', this, rec, ix[0]);
20108         
20109         var lw = Math.floor(
20110              (
20111                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
20112              ) / this.maxColumns
20113         );
20114         this.loadingChildren = true;
20115         this.stores[opts.list+1].loadDataFromChildren( rec );
20116         this.loadingChildren = false;
20117         var dl = this.stores[opts.list+1]. getTotalCount();
20118         
20119         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
20120         
20121         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
20122         for (var i = opts.list+2; i < this.views.length;i++) {
20123             this.views[i].getEl().setStyle({ display : 'none' });
20124         }
20125         
20126         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
20127         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
20128         
20129         if (this.isLoading) {
20130            // this.selectActive(opts.list);
20131         }
20132          
20133     },
20134     
20135     
20136     
20137     
20138     onDoubleClick : function()
20139     {
20140         this.collapse(); //??
20141     },
20142     
20143      
20144     
20145     
20146     
20147     // private
20148     recordToStack : function(store, prop, value, stack)
20149     {
20150         var cstore = new Roo.data.SimpleStore({
20151             //fields : this.store.reader.meta.fields, // we need array reader.. for
20152             reader : this.store.reader,
20153             data : [ ]
20154         });
20155         var _this = this;
20156         var record  = false;
20157         var srec = false;
20158         if(store.getCount() < 1){
20159             return false;
20160         }
20161         store.each(function(r){
20162             if(r.data[prop] == value){
20163                 record = r;
20164             srec = r;
20165                 return false;
20166             }
20167             if (r.data.cn && r.data.cn.length) {
20168                 cstore.loadDataFromChildren( r);
20169                 var cret = _this.recordToStack(cstore, prop, value, stack);
20170                 if (cret !== false) {
20171                     record = cret;
20172                     srec = r;
20173                     return false;
20174                 }
20175             }
20176              
20177             return true;
20178         });
20179         if (record == false) {
20180             return false
20181         }
20182         stack.unshift(srec);
20183         return record;
20184     },
20185     
20186     /*
20187      * find the stack of stores that match our value.
20188      *
20189      * 
20190      */
20191     
20192     selectActive : function ()
20193     {
20194         // if store is not loaded, then we will need to wait for that to happen first.
20195         var stack = [];
20196         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
20197         for (var i = 0; i < stack.length; i++ ) {
20198             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
20199         }
20200         
20201     }
20202         
20203          
20204     
20205     
20206     
20207     
20208 });/*
20209  * Based on:
20210  * Ext JS Library 1.1.1
20211  * Copyright(c) 2006-2007, Ext JS, LLC.
20212  *
20213  * Originally Released Under LGPL - original licence link has changed is not relivant.
20214  *
20215  * Fork - LGPL
20216  * <script type="text/javascript">
20217  */
20218 /**
20219  * @class Roo.form.Checkbox
20220  * @extends Roo.form.Field
20221  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
20222  * @constructor
20223  * Creates a new Checkbox
20224  * @param {Object} config Configuration options
20225  */
20226 Roo.form.Checkbox = function(config){
20227     Roo.form.Checkbox.superclass.constructor.call(this, config);
20228     this.addEvents({
20229         /**
20230          * @event check
20231          * Fires when the checkbox is checked or unchecked.
20232              * @param {Roo.form.Checkbox} this This checkbox
20233              * @param {Boolean} checked The new checked value
20234              */
20235         check : true
20236     });
20237 };
20238
20239 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
20240     /**
20241      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
20242      */
20243     focusClass : undefined,
20244     /**
20245      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
20246      */
20247     fieldClass: "x-form-field",
20248     /**
20249      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
20250      */
20251     checked: false,
20252     /**
20253      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
20254      * {tag: "input", type: "checkbox", autocomplete: "off"})
20255      */
20256     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
20257     /**
20258      * @cfg {String} boxLabel The text that appears beside the checkbox
20259      */
20260     boxLabel : "",
20261     /**
20262      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
20263      */  
20264     inputValue : '1',
20265     /**
20266      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20267      */
20268      valueOff: '0', // value when not checked..
20269
20270     actionMode : 'viewEl', 
20271     //
20272     // private
20273     itemCls : 'x-menu-check-item x-form-item',
20274     groupClass : 'x-menu-group-item',
20275     inputType : 'hidden',
20276     
20277     
20278     inSetChecked: false, // check that we are not calling self...
20279     
20280     inputElement: false, // real input element?
20281     basedOn: false, // ????
20282     
20283     isFormField: true, // not sure where this is needed!!!!
20284
20285     onResize : function(){
20286         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
20287         if(!this.boxLabel){
20288             this.el.alignTo(this.wrap, 'c-c');
20289         }
20290     },
20291
20292     initEvents : function(){
20293         Roo.form.Checkbox.superclass.initEvents.call(this);
20294         this.el.on("click", this.onClick,  this);
20295         this.el.on("change", this.onClick,  this);
20296     },
20297
20298
20299     getResizeEl : function(){
20300         return this.wrap;
20301     },
20302
20303     getPositionEl : function(){
20304         return this.wrap;
20305     },
20306
20307     // private
20308     onRender : function(ct, position){
20309         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20310         /*
20311         if(this.inputValue !== undefined){
20312             this.el.dom.value = this.inputValue;
20313         }
20314         */
20315         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20316         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20317         var viewEl = this.wrap.createChild({ 
20318             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20319         this.viewEl = viewEl;   
20320         this.wrap.on('click', this.onClick,  this); 
20321         
20322         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20323         this.el.on('propertychange', this.setFromHidden,  this);  //ie
20324         
20325         
20326         
20327         if(this.boxLabel){
20328             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20329         //    viewEl.on('click', this.onClick,  this); 
20330         }
20331         //if(this.checked){
20332             this.setChecked(this.checked);
20333         //}else{
20334             //this.checked = this.el.dom;
20335         //}
20336
20337     },
20338
20339     // private
20340     initValue : Roo.emptyFn,
20341
20342     /**
20343      * Returns the checked state of the checkbox.
20344      * @return {Boolean} True if checked, else false
20345      */
20346     getValue : function(){
20347         if(this.el){
20348             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
20349         }
20350         return this.valueOff;
20351         
20352     },
20353
20354         // private
20355     onClick : function(){ 
20356         if (this.disabled) {
20357             return;
20358         }
20359         this.setChecked(!this.checked);
20360
20361         //if(this.el.dom.checked != this.checked){
20362         //    this.setValue(this.el.dom.checked);
20363        // }
20364     },
20365
20366     /**
20367      * Sets the checked state of the checkbox.
20368      * On is always based on a string comparison between inputValue and the param.
20369      * @param {Boolean/String} value - the value to set 
20370      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
20371      */
20372     setValue : function(v,suppressEvent){
20373         
20374         
20375         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
20376         //if(this.el && this.el.dom){
20377         //    this.el.dom.checked = this.checked;
20378         //    this.el.dom.defaultChecked = this.checked;
20379         //}
20380         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
20381         //this.fireEvent("check", this, this.checked);
20382     },
20383     // private..
20384     setChecked : function(state,suppressEvent)
20385     {
20386         if (this.inSetChecked) {
20387             this.checked = state;
20388             return;
20389         }
20390         
20391     
20392         if(this.wrap){
20393             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
20394         }
20395         this.checked = state;
20396         if(suppressEvent !== true){
20397             this.fireEvent('check', this, state);
20398         }
20399         this.inSetChecked = true;
20400         this.el.dom.value = state ? this.inputValue : this.valueOff;
20401         this.inSetChecked = false;
20402         
20403     },
20404     // handle setting of hidden value by some other method!!?!?
20405     setFromHidden: function()
20406     {
20407         if(!this.el){
20408             return;
20409         }
20410         //console.log("SET FROM HIDDEN");
20411         //alert('setFrom hidden');
20412         this.setValue(this.el.dom.value);
20413     },
20414     
20415     onDestroy : function()
20416     {
20417         if(this.viewEl){
20418             Roo.get(this.viewEl).remove();
20419         }
20420          
20421         Roo.form.Checkbox.superclass.onDestroy.call(this);
20422     },
20423     
20424     setBoxLabel : function(str)
20425     {
20426         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
20427     }
20428
20429 });/*
20430  * Based on:
20431  * Ext JS Library 1.1.1
20432  * Copyright(c) 2006-2007, Ext JS, LLC.
20433  *
20434  * Originally Released Under LGPL - original licence link has changed is not relivant.
20435  *
20436  * Fork - LGPL
20437  * <script type="text/javascript">
20438  */
20439  
20440 /**
20441  * @class Roo.form.Radio
20442  * @extends Roo.form.Checkbox
20443  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
20444  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
20445  * @constructor
20446  * Creates a new Radio
20447  * @param {Object} config Configuration options
20448  */
20449 Roo.form.Radio = function(){
20450     Roo.form.Radio.superclass.constructor.apply(this, arguments);
20451 };
20452 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
20453     inputType: 'radio',
20454
20455     /**
20456      * If this radio is part of a group, it will return the selected value
20457      * @return {String}
20458      */
20459     getGroupValue : function(){
20460         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
20461     },
20462     
20463     
20464     onRender : function(ct, position){
20465         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
20466         
20467         if(this.inputValue !== undefined){
20468             this.el.dom.value = this.inputValue;
20469         }
20470          
20471         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
20472         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
20473         //var viewEl = this.wrap.createChild({ 
20474         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
20475         //this.viewEl = viewEl;   
20476         //this.wrap.on('click', this.onClick,  this); 
20477         
20478         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
20479         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
20480         
20481         
20482         
20483         if(this.boxLabel){
20484             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
20485         //    viewEl.on('click', this.onClick,  this); 
20486         }
20487          if(this.checked){
20488             this.el.dom.checked =   'checked' ;
20489         }
20490          
20491     } 
20492     
20493     
20494 });//<script type="text/javascript">
20495
20496 /*
20497  * Based  Ext JS Library 1.1.1
20498  * Copyright(c) 2006-2007, Ext JS, LLC.
20499  * LGPL
20500  *
20501  */
20502  
20503 /**
20504  * @class Roo.HtmlEditorCore
20505  * @extends Roo.Component
20506  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20507  *
20508  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20509  */
20510
20511 Roo.HtmlEditorCore = function(config){
20512     
20513     
20514     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20515     
20516     
20517     this.addEvents({
20518         /**
20519          * @event initialize
20520          * Fires when the editor is fully initialized (including the iframe)
20521          * @param {Roo.HtmlEditorCore} this
20522          */
20523         initialize: true,
20524         /**
20525          * @event activate
20526          * Fires when the editor is first receives the focus. Any insertion must wait
20527          * until after this event.
20528          * @param {Roo.HtmlEditorCore} this
20529          */
20530         activate: true,
20531          /**
20532          * @event beforesync
20533          * Fires before the textarea is updated with content from the editor iframe. Return false
20534          * to cancel the sync.
20535          * @param {Roo.HtmlEditorCore} this
20536          * @param {String} html
20537          */
20538         beforesync: true,
20539          /**
20540          * @event beforepush
20541          * Fires before the iframe editor is updated with content from the textarea. Return false
20542          * to cancel the push.
20543          * @param {Roo.HtmlEditorCore} this
20544          * @param {String} html
20545          */
20546         beforepush: true,
20547          /**
20548          * @event sync
20549          * Fires when the textarea is updated with content from the editor iframe.
20550          * @param {Roo.HtmlEditorCore} this
20551          * @param {String} html
20552          */
20553         sync: true,
20554          /**
20555          * @event push
20556          * Fires when the iframe editor is updated with content from the textarea.
20557          * @param {Roo.HtmlEditorCore} this
20558          * @param {String} html
20559          */
20560         push: true,
20561         
20562         /**
20563          * @event editorevent
20564          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20565          * @param {Roo.HtmlEditorCore} this
20566          */
20567         editorevent: true
20568         
20569     });
20570     
20571     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20572     
20573     // defaults : white / black...
20574     this.applyBlacklists();
20575     
20576     
20577     
20578 };
20579
20580
20581 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
20582
20583
20584      /**
20585      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
20586      */
20587     
20588     owner : false,
20589     
20590      /**
20591      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
20592      *                        Roo.resizable.
20593      */
20594     resizable : false,
20595      /**
20596      * @cfg {Number} height (in pixels)
20597      */   
20598     height: 300,
20599    /**
20600      * @cfg {Number} width (in pixels)
20601      */   
20602     width: 500,
20603     
20604     /**
20605      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20606      * 
20607      */
20608     stylesheets: false,
20609     
20610     // id of frame..
20611     frameId: false,
20612     
20613     // private properties
20614     validationEvent : false,
20615     deferHeight: true,
20616     initialized : false,
20617     activated : false,
20618     sourceEditMode : false,
20619     onFocus : Roo.emptyFn,
20620     iframePad:3,
20621     hideMode:'offsets',
20622     
20623     clearUp: true,
20624     
20625     // blacklist + whitelisted elements..
20626     black: false,
20627     white: false,
20628      
20629     bodyCls : '',
20630
20631     /**
20632      * Protected method that will not generally be called directly. It
20633      * is called when the editor initializes the iframe with HTML contents. Override this method if you
20634      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20635      */
20636     getDocMarkup : function(){
20637         // body styles..
20638         var st = '';
20639         
20640         // inherit styels from page...?? 
20641         if (this.stylesheets === false) {
20642             
20643             Roo.get(document.head).select('style').each(function(node) {
20644                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20645             });
20646             
20647             Roo.get(document.head).select('link').each(function(node) { 
20648                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20649             });
20650             
20651         } else if (!this.stylesheets.length) {
20652                 // simple..
20653                 st = '<style type="text/css">' +
20654                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20655                    '</style>';
20656         } else {
20657             for (var i in this.stylesheets) { 
20658                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
20659             }
20660             
20661         }
20662         
20663         st +=  '<style type="text/css">' +
20664             'IMG { cursor: pointer } ' +
20665         '</style>';
20666
20667         var cls = 'roo-htmleditor-body';
20668         
20669         if(this.bodyCls.length){
20670             cls += ' ' + this.bodyCls;
20671         }
20672         
20673         return '<html><head>' + st  +
20674             //<style type="text/css">' +
20675             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20676             //'</style>' +
20677             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20678     },
20679
20680     // private
20681     onRender : function(ct, position)
20682     {
20683         var _t = this;
20684         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20685         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20686         
20687         
20688         this.el.dom.style.border = '0 none';
20689         this.el.dom.setAttribute('tabIndex', -1);
20690         this.el.addClass('x-hidden hide');
20691         
20692         
20693         
20694         if(Roo.isIE){ // fix IE 1px bogus margin
20695             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20696         }
20697        
20698         
20699         this.frameId = Roo.id();
20700         
20701          
20702         
20703         var iframe = this.owner.wrap.createChild({
20704             tag: 'iframe',
20705             cls: 'form-control', // bootstrap..
20706             id: this.frameId,
20707             name: this.frameId,
20708             frameBorder : 'no',
20709             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20710         }, this.el
20711         );
20712         
20713         
20714         this.iframe = iframe.dom;
20715
20716          this.assignDocWin();
20717         
20718         this.doc.designMode = 'on';
20719        
20720         this.doc.open();
20721         this.doc.write(this.getDocMarkup());
20722         this.doc.close();
20723
20724         
20725         var task = { // must defer to wait for browser to be ready
20726             run : function(){
20727                 //console.log("run task?" + this.doc.readyState);
20728                 this.assignDocWin();
20729                 if(this.doc.body || this.doc.readyState == 'complete'){
20730                     try {
20731                         this.doc.designMode="on";
20732                     } catch (e) {
20733                         return;
20734                     }
20735                     Roo.TaskMgr.stop(task);
20736                     this.initEditor.defer(10, this);
20737                 }
20738             },
20739             interval : 10,
20740             duration: 10000,
20741             scope: this
20742         };
20743         Roo.TaskMgr.start(task);
20744
20745     },
20746
20747     // private
20748     onResize : function(w, h)
20749     {
20750          Roo.log('resize: ' +w + ',' + h );
20751         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20752         if(!this.iframe){
20753             return;
20754         }
20755         if(typeof w == 'number'){
20756             
20757             this.iframe.style.width = w + 'px';
20758         }
20759         if(typeof h == 'number'){
20760             
20761             this.iframe.style.height = h + 'px';
20762             if(this.doc){
20763                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20764             }
20765         }
20766         
20767     },
20768
20769     /**
20770      * Toggles the editor between standard and source edit mode.
20771      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20772      */
20773     toggleSourceEdit : function(sourceEditMode){
20774         
20775         this.sourceEditMode = sourceEditMode === true;
20776         
20777         if(this.sourceEditMode){
20778  
20779             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20780             
20781         }else{
20782             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20783             //this.iframe.className = '';
20784             this.deferFocus();
20785         }
20786         //this.setSize(this.owner.wrap.getSize());
20787         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20788     },
20789
20790     
20791   
20792
20793     /**
20794      * Protected method that will not generally be called directly. If you need/want
20795      * custom HTML cleanup, this is the method you should override.
20796      * @param {String} html The HTML to be cleaned
20797      * return {String} The cleaned HTML
20798      */
20799     cleanHtml : function(html){
20800         html = String(html);
20801         if(html.length > 5){
20802             if(Roo.isSafari){ // strip safari nonsense
20803                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20804             }
20805         }
20806         if(html == '&nbsp;'){
20807             html = '';
20808         }
20809         return html;
20810     },
20811
20812     /**
20813      * HTML Editor -> Textarea
20814      * Protected method that will not generally be called directly. Syncs the contents
20815      * of the editor iframe with the textarea.
20816      */
20817     syncValue : function(){
20818         if(this.initialized){
20819             var bd = (this.doc.body || this.doc.documentElement);
20820             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20821             var html = bd.innerHTML;
20822             if(Roo.isSafari){
20823                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20824                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20825                 if(m && m[1]){
20826                     html = '<div style="'+m[0]+'">' + html + '</div>';
20827                 }
20828             }
20829             html = this.cleanHtml(html);
20830             // fix up the special chars.. normaly like back quotes in word...
20831             // however we do not want to do this with chinese..
20832             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20833                 
20834                 var cc = match.charCodeAt();
20835
20836                 // Get the character value, handling surrogate pairs
20837                 if (match.length == 2) {
20838                     // It's a surrogate pair, calculate the Unicode code point
20839                     var high = match.charCodeAt(0) - 0xD800;
20840                     var low  = match.charCodeAt(1) - 0xDC00;
20841                     cc = (high * 0x400) + low + 0x10000;
20842                 }  else if (
20843                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20844                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20845                     (cc >= 0xf900 && cc < 0xfb00 )
20846                 ) {
20847                         return match;
20848                 }  
20849          
20850                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20851                 return "&#" + cc + ";";
20852                 
20853                 
20854             });
20855             
20856             
20857              
20858             if(this.owner.fireEvent('beforesync', this, html) !== false){
20859                 this.el.dom.value = html;
20860                 this.owner.fireEvent('sync', this, html);
20861             }
20862         }
20863     },
20864
20865     /**
20866      * Protected method that will not generally be called directly. Pushes the value of the textarea
20867      * into the iframe editor.
20868      */
20869     pushValue : function(){
20870         if(this.initialized){
20871             var v = this.el.dom.value.trim();
20872             
20873 //            if(v.length < 1){
20874 //                v = '&#160;';
20875 //            }
20876             
20877             if(this.owner.fireEvent('beforepush', this, v) !== false){
20878                 var d = (this.doc.body || this.doc.documentElement);
20879                 d.innerHTML = v;
20880                 this.cleanUpPaste();
20881                 this.el.dom.value = d.innerHTML;
20882                 this.owner.fireEvent('push', this, v);
20883             }
20884         }
20885     },
20886
20887     // private
20888     deferFocus : function(){
20889         this.focus.defer(10, this);
20890     },
20891
20892     // doc'ed in Field
20893     focus : function(){
20894         if(this.win && !this.sourceEditMode){
20895             this.win.focus();
20896         }else{
20897             this.el.focus();
20898         }
20899     },
20900     
20901     assignDocWin: function()
20902     {
20903         var iframe = this.iframe;
20904         
20905          if(Roo.isIE){
20906             this.doc = iframe.contentWindow.document;
20907             this.win = iframe.contentWindow;
20908         } else {
20909 //            if (!Roo.get(this.frameId)) {
20910 //                return;
20911 //            }
20912 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20913 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20914             
20915             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20916                 return;
20917             }
20918             
20919             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20920             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20921         }
20922     },
20923     
20924     // private
20925     initEditor : function(){
20926         //console.log("INIT EDITOR");
20927         this.assignDocWin();
20928         
20929         
20930         
20931         this.doc.designMode="on";
20932         this.doc.open();
20933         this.doc.write(this.getDocMarkup());
20934         this.doc.close();
20935         
20936         var dbody = (this.doc.body || this.doc.documentElement);
20937         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20938         // this copies styles from the containing element into thsi one..
20939         // not sure why we need all of this..
20940         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20941         
20942         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20943         //ss['background-attachment'] = 'fixed'; // w3c
20944         dbody.bgProperties = 'fixed'; // ie
20945         //Roo.DomHelper.applyStyles(dbody, ss);
20946         Roo.EventManager.on(this.doc, {
20947             //'mousedown': this.onEditorEvent,
20948             'mouseup': this.onEditorEvent,
20949             'dblclick': this.onEditorEvent,
20950             'click': this.onEditorEvent,
20951             'keyup': this.onEditorEvent,
20952             buffer:100,
20953             scope: this
20954         });
20955         if(Roo.isGecko){
20956             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20957         }
20958         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20959             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20960         }
20961         this.initialized = true;
20962
20963         this.owner.fireEvent('initialize', this);
20964         this.pushValue();
20965     },
20966
20967     // private
20968     onDestroy : function(){
20969         
20970         
20971         
20972         if(this.rendered){
20973             
20974             //for (var i =0; i < this.toolbars.length;i++) {
20975             //    // fixme - ask toolbars for heights?
20976             //    this.toolbars[i].onDestroy();
20977            // }
20978             
20979             //this.wrap.dom.innerHTML = '';
20980             //this.wrap.remove();
20981         }
20982     },
20983
20984     // private
20985     onFirstFocus : function(){
20986         
20987         this.assignDocWin();
20988         
20989         
20990         this.activated = true;
20991          
20992     
20993         if(Roo.isGecko){ // prevent silly gecko errors
20994             this.win.focus();
20995             var s = this.win.getSelection();
20996             if(!s.focusNode || s.focusNode.nodeType != 3){
20997                 var r = s.getRangeAt(0);
20998                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20999                 r.collapse(true);
21000                 this.deferFocus();
21001             }
21002             try{
21003                 this.execCmd('useCSS', true);
21004                 this.execCmd('styleWithCSS', false);
21005             }catch(e){}
21006         }
21007         this.owner.fireEvent('activate', this);
21008     },
21009
21010     // private
21011     adjustFont: function(btn){
21012         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21013         //if(Roo.isSafari){ // safari
21014         //    adjust *= 2;
21015        // }
21016         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21017         if(Roo.isSafari){ // safari
21018             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21019             v =  (v < 10) ? 10 : v;
21020             v =  (v > 48) ? 48 : v;
21021             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21022             
21023         }
21024         
21025         
21026         v = Math.max(1, v+adjust);
21027         
21028         this.execCmd('FontSize', v  );
21029     },
21030
21031     onEditorEvent : function(e)
21032     {
21033         this.owner.fireEvent('editorevent', this, e);
21034       //  this.updateToolbar();
21035         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21036     },
21037
21038     insertTag : function(tg)
21039     {
21040         // could be a bit smarter... -> wrap the current selected tRoo..
21041         if (tg.toLowerCase() == 'span' ||
21042             tg.toLowerCase() == 'code' ||
21043             tg.toLowerCase() == 'sup' ||
21044             tg.toLowerCase() == 'sub' 
21045             ) {
21046             
21047             range = this.createRange(this.getSelection());
21048             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21049             wrappingNode.appendChild(range.extractContents());
21050             range.insertNode(wrappingNode);
21051
21052             return;
21053             
21054             
21055             
21056         }
21057         this.execCmd("formatblock",   tg);
21058         
21059     },
21060     
21061     insertText : function(txt)
21062     {
21063         
21064         
21065         var range = this.createRange();
21066         range.deleteContents();
21067                //alert(Sender.getAttribute('label'));
21068                
21069         range.insertNode(this.doc.createTextNode(txt));
21070     } ,
21071     
21072      
21073
21074     /**
21075      * Executes a Midas editor command on the editor document and performs necessary focus and
21076      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21077      * @param {String} cmd The Midas command
21078      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21079      */
21080     relayCmd : function(cmd, value){
21081         this.win.focus();
21082         this.execCmd(cmd, value);
21083         this.owner.fireEvent('editorevent', this);
21084         //this.updateToolbar();
21085         this.owner.deferFocus();
21086     },
21087
21088     /**
21089      * Executes a Midas editor command directly on the editor document.
21090      * For visual commands, you should use {@link #relayCmd} instead.
21091      * <b>This should only be called after the editor is initialized.</b>
21092      * @param {String} cmd The Midas command
21093      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21094      */
21095     execCmd : function(cmd, value){
21096         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21097         this.syncValue();
21098     },
21099  
21100  
21101    
21102     /**
21103      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21104      * to insert tRoo.
21105      * @param {String} text | dom node.. 
21106      */
21107     insertAtCursor : function(text)
21108     {
21109         
21110         if(!this.activated){
21111             return;
21112         }
21113         /*
21114         if(Roo.isIE){
21115             this.win.focus();
21116             var r = this.doc.selection.createRange();
21117             if(r){
21118                 r.collapse(true);
21119                 r.pasteHTML(text);
21120                 this.syncValue();
21121                 this.deferFocus();
21122             
21123             }
21124             return;
21125         }
21126         */
21127         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21128             this.win.focus();
21129             
21130             
21131             // from jquery ui (MIT licenced)
21132             var range, node;
21133             var win = this.win;
21134             
21135             if (win.getSelection && win.getSelection().getRangeAt) {
21136                 range = win.getSelection().getRangeAt(0);
21137                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21138                 range.insertNode(node);
21139             } else if (win.document.selection && win.document.selection.createRange) {
21140                 // no firefox support
21141                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21142                 win.document.selection.createRange().pasteHTML(txt);
21143             } else {
21144                 // no firefox support
21145                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21146                 this.execCmd('InsertHTML', txt);
21147             } 
21148             
21149             this.syncValue();
21150             
21151             this.deferFocus();
21152         }
21153     },
21154  // private
21155     mozKeyPress : function(e){
21156         if(e.ctrlKey){
21157             var c = e.getCharCode(), cmd;
21158           
21159             if(c > 0){
21160                 c = String.fromCharCode(c).toLowerCase();
21161                 switch(c){
21162                     case 'b':
21163                         cmd = 'bold';
21164                         break;
21165                     case 'i':
21166                         cmd = 'italic';
21167                         break;
21168                     
21169                     case 'u':
21170                         cmd = 'underline';
21171                         break;
21172                     
21173                     case 'v':
21174                         this.cleanUpPaste.defer(100, this);
21175                         return;
21176                         
21177                 }
21178                 if(cmd){
21179                     this.win.focus();
21180                     this.execCmd(cmd);
21181                     this.deferFocus();
21182                     e.preventDefault();
21183                 }
21184                 
21185             }
21186         }
21187     },
21188
21189     // private
21190     fixKeys : function(){ // load time branching for fastest keydown performance
21191         if(Roo.isIE){
21192             return function(e){
21193                 var k = e.getKey(), r;
21194                 if(k == e.TAB){
21195                     e.stopEvent();
21196                     r = this.doc.selection.createRange();
21197                     if(r){
21198                         r.collapse(true);
21199                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21200                         this.deferFocus();
21201                     }
21202                     return;
21203                 }
21204                 
21205                 if(k == e.ENTER){
21206                     r = this.doc.selection.createRange();
21207                     if(r){
21208                         var target = r.parentElement();
21209                         if(!target || target.tagName.toLowerCase() != 'li'){
21210                             e.stopEvent();
21211                             r.pasteHTML('<br />');
21212                             r.collapse(false);
21213                             r.select();
21214                         }
21215                     }
21216                 }
21217                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21218                     this.cleanUpPaste.defer(100, this);
21219                     return;
21220                 }
21221                 
21222                 
21223             };
21224         }else if(Roo.isOpera){
21225             return function(e){
21226                 var k = e.getKey();
21227                 if(k == e.TAB){
21228                     e.stopEvent();
21229                     this.win.focus();
21230                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21231                     this.deferFocus();
21232                 }
21233                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21234                     this.cleanUpPaste.defer(100, this);
21235                     return;
21236                 }
21237                 
21238             };
21239         }else if(Roo.isSafari){
21240             return function(e){
21241                 var k = e.getKey();
21242                 
21243                 if(k == e.TAB){
21244                     e.stopEvent();
21245                     this.execCmd('InsertText','\t');
21246                     this.deferFocus();
21247                     return;
21248                 }
21249                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21250                     this.cleanUpPaste.defer(100, this);
21251                     return;
21252                 }
21253                 
21254              };
21255         }
21256     }(),
21257     
21258     getAllAncestors: function()
21259     {
21260         var p = this.getSelectedNode();
21261         var a = [];
21262         if (!p) {
21263             a.push(p); // push blank onto stack..
21264             p = this.getParentElement();
21265         }
21266         
21267         
21268         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21269             a.push(p);
21270             p = p.parentNode;
21271         }
21272         a.push(this.doc.body);
21273         return a;
21274     },
21275     lastSel : false,
21276     lastSelNode : false,
21277     
21278     
21279     getSelection : function() 
21280     {
21281         this.assignDocWin();
21282         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21283     },
21284     
21285     getSelectedNode: function() 
21286     {
21287         // this may only work on Gecko!!!
21288         
21289         // should we cache this!!!!
21290         
21291         
21292         
21293          
21294         var range = this.createRange(this.getSelection()).cloneRange();
21295         
21296         if (Roo.isIE) {
21297             var parent = range.parentElement();
21298             while (true) {
21299                 var testRange = range.duplicate();
21300                 testRange.moveToElementText(parent);
21301                 if (testRange.inRange(range)) {
21302                     break;
21303                 }
21304                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21305                     break;
21306                 }
21307                 parent = parent.parentElement;
21308             }
21309             return parent;
21310         }
21311         
21312         // is ancestor a text element.
21313         var ac =  range.commonAncestorContainer;
21314         if (ac.nodeType == 3) {
21315             ac = ac.parentNode;
21316         }
21317         
21318         var ar = ac.childNodes;
21319          
21320         var nodes = [];
21321         var other_nodes = [];
21322         var has_other_nodes = false;
21323         for (var i=0;i<ar.length;i++) {
21324             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21325                 continue;
21326             }
21327             // fullly contained node.
21328             
21329             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21330                 nodes.push(ar[i]);
21331                 continue;
21332             }
21333             
21334             // probably selected..
21335             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21336                 other_nodes.push(ar[i]);
21337                 continue;
21338             }
21339             // outer..
21340             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21341                 continue;
21342             }
21343             
21344             
21345             has_other_nodes = true;
21346         }
21347         if (!nodes.length && other_nodes.length) {
21348             nodes= other_nodes;
21349         }
21350         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21351             return false;
21352         }
21353         
21354         return nodes[0];
21355     },
21356     createRange: function(sel)
21357     {
21358         // this has strange effects when using with 
21359         // top toolbar - not sure if it's a great idea.
21360         //this.editor.contentWindow.focus();
21361         if (typeof sel != "undefined") {
21362             try {
21363                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21364             } catch(e) {
21365                 return this.doc.createRange();
21366             }
21367         } else {
21368             return this.doc.createRange();
21369         }
21370     },
21371     getParentElement: function()
21372     {
21373         
21374         this.assignDocWin();
21375         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21376         
21377         var range = this.createRange(sel);
21378          
21379         try {
21380             var p = range.commonAncestorContainer;
21381             while (p.nodeType == 3) { // text node
21382                 p = p.parentNode;
21383             }
21384             return p;
21385         } catch (e) {
21386             return null;
21387         }
21388     
21389     },
21390     /***
21391      *
21392      * Range intersection.. the hard stuff...
21393      *  '-1' = before
21394      *  '0' = hits..
21395      *  '1' = after.
21396      *         [ -- selected range --- ]
21397      *   [fail]                        [fail]
21398      *
21399      *    basically..
21400      *      if end is before start or  hits it. fail.
21401      *      if start is after end or hits it fail.
21402      *
21403      *   if either hits (but other is outside. - then it's not 
21404      *   
21405      *    
21406      **/
21407     
21408     
21409     // @see http://www.thismuchiknow.co.uk/?p=64.
21410     rangeIntersectsNode : function(range, node)
21411     {
21412         var nodeRange = node.ownerDocument.createRange();
21413         try {
21414             nodeRange.selectNode(node);
21415         } catch (e) {
21416             nodeRange.selectNodeContents(node);
21417         }
21418     
21419         var rangeStartRange = range.cloneRange();
21420         rangeStartRange.collapse(true);
21421     
21422         var rangeEndRange = range.cloneRange();
21423         rangeEndRange.collapse(false);
21424     
21425         var nodeStartRange = nodeRange.cloneRange();
21426         nodeStartRange.collapse(true);
21427     
21428         var nodeEndRange = nodeRange.cloneRange();
21429         nodeEndRange.collapse(false);
21430     
21431         return rangeStartRange.compareBoundaryPoints(
21432                  Range.START_TO_START, nodeEndRange) == -1 &&
21433                rangeEndRange.compareBoundaryPoints(
21434                  Range.START_TO_START, nodeStartRange) == 1;
21435         
21436          
21437     },
21438     rangeCompareNode : function(range, node)
21439     {
21440         var nodeRange = node.ownerDocument.createRange();
21441         try {
21442             nodeRange.selectNode(node);
21443         } catch (e) {
21444             nodeRange.selectNodeContents(node);
21445         }
21446         
21447         
21448         range.collapse(true);
21449     
21450         nodeRange.collapse(true);
21451      
21452         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21453         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21454          
21455         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21456         
21457         var nodeIsBefore   =  ss == 1;
21458         var nodeIsAfter    = ee == -1;
21459         
21460         if (nodeIsBefore && nodeIsAfter) {
21461             return 0; // outer
21462         }
21463         if (!nodeIsBefore && nodeIsAfter) {
21464             return 1; //right trailed.
21465         }
21466         
21467         if (nodeIsBefore && !nodeIsAfter) {
21468             return 2;  // left trailed.
21469         }
21470         // fully contined.
21471         return 3;
21472     },
21473
21474     // private? - in a new class?
21475     cleanUpPaste :  function()
21476     {
21477         // cleans up the whole document..
21478         Roo.log('cleanuppaste');
21479         
21480         this.cleanUpChildren(this.doc.body);
21481         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21482         if (clean != this.doc.body.innerHTML) {
21483             this.doc.body.innerHTML = clean;
21484         }
21485         
21486     },
21487     
21488     cleanWordChars : function(input) {// change the chars to hex code
21489         var he = Roo.HtmlEditorCore;
21490         
21491         var output = input;
21492         Roo.each(he.swapCodes, function(sw) { 
21493             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21494             
21495             output = output.replace(swapper, sw[1]);
21496         });
21497         
21498         return output;
21499     },
21500     
21501     
21502     cleanUpChildren : function (n)
21503     {
21504         if (!n.childNodes.length) {
21505             return;
21506         }
21507         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21508            this.cleanUpChild(n.childNodes[i]);
21509         }
21510     },
21511     
21512     
21513         
21514     
21515     cleanUpChild : function (node)
21516     {
21517         var ed = this;
21518         //console.log(node);
21519         if (node.nodeName == "#text") {
21520             // clean up silly Windows -- stuff?
21521             return; 
21522         }
21523         if (node.nodeName == "#comment") {
21524             node.parentNode.removeChild(node);
21525             // clean up silly Windows -- stuff?
21526             return; 
21527         }
21528         var lcname = node.tagName.toLowerCase();
21529         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21530         // whitelist of tags..
21531         
21532         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21533             // remove node.
21534             node.parentNode.removeChild(node);
21535             return;
21536             
21537         }
21538         
21539         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21540         
21541         // spans with no attributes - just remove them..
21542         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21543             remove_keep_children = true;
21544         }
21545         
21546         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21547         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21548         
21549         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21550         //    remove_keep_children = true;
21551         //}
21552         
21553         if (remove_keep_children) {
21554             this.cleanUpChildren(node);
21555             // inserts everything just before this node...
21556             while (node.childNodes.length) {
21557                 var cn = node.childNodes[0];
21558                 node.removeChild(cn);
21559                 node.parentNode.insertBefore(cn, node);
21560             }
21561             node.parentNode.removeChild(node);
21562             return;
21563         }
21564         
21565         if (!node.attributes || !node.attributes.length) {
21566             
21567           
21568             
21569             
21570             this.cleanUpChildren(node);
21571             return;
21572         }
21573         
21574         function cleanAttr(n,v)
21575         {
21576             
21577             if (v.match(/^\./) || v.match(/^\//)) {
21578                 return;
21579             }
21580             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21581                 return;
21582             }
21583             if (v.match(/^#/)) {
21584                 return;
21585             }
21586             if (v.match(/^\{/)) { // allow template editing.
21587                 return;
21588             }
21589 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21590             node.removeAttribute(n);
21591             
21592         }
21593         
21594         var cwhite = this.cwhite;
21595         var cblack = this.cblack;
21596             
21597         function cleanStyle(n,v)
21598         {
21599             if (v.match(/expression/)) { //XSS?? should we even bother..
21600                 node.removeAttribute(n);
21601                 return;
21602             }
21603             
21604             var parts = v.split(/;/);
21605             var clean = [];
21606             
21607             Roo.each(parts, function(p) {
21608                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21609                 if (!p.length) {
21610                     return true;
21611                 }
21612                 var l = p.split(':').shift().replace(/\s+/g,'');
21613                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21614                 
21615                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21616 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21617                     //node.removeAttribute(n);
21618                     return true;
21619                 }
21620                 //Roo.log()
21621                 // only allow 'c whitelisted system attributes'
21622                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21623 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21624                     //node.removeAttribute(n);
21625                     return true;
21626                 }
21627                 
21628                 
21629                  
21630                 
21631                 clean.push(p);
21632                 return true;
21633             });
21634             if (clean.length) { 
21635                 node.setAttribute(n, clean.join(';'));
21636             } else {
21637                 node.removeAttribute(n);
21638             }
21639             
21640         }
21641         
21642         
21643         for (var i = node.attributes.length-1; i > -1 ; i--) {
21644             var a = node.attributes[i];
21645             //console.log(a);
21646             
21647             if (a.name.toLowerCase().substr(0,2)=='on')  {
21648                 node.removeAttribute(a.name);
21649                 continue;
21650             }
21651             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21652                 node.removeAttribute(a.name);
21653                 continue;
21654             }
21655             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21656                 cleanAttr(a.name,a.value); // fixme..
21657                 continue;
21658             }
21659             if (a.name == 'style') {
21660                 cleanStyle(a.name,a.value);
21661                 continue;
21662             }
21663             /// clean up MS crap..
21664             // tecnically this should be a list of valid class'es..
21665             
21666             
21667             if (a.name == 'class') {
21668                 if (a.value.match(/^Mso/)) {
21669                     node.removeAttribute('class');
21670                 }
21671                 
21672                 if (a.value.match(/^body$/)) {
21673                     node.removeAttribute('class');
21674                 }
21675                 continue;
21676             }
21677             
21678             // style cleanup!?
21679             // class cleanup?
21680             
21681         }
21682         
21683         
21684         this.cleanUpChildren(node);
21685         
21686         
21687     },
21688     
21689     /**
21690      * Clean up MS wordisms...
21691      */
21692     cleanWord : function(node)
21693     {
21694         if (!node) {
21695             this.cleanWord(this.doc.body);
21696             return;
21697         }
21698         
21699         if(
21700                 node.nodeName == 'SPAN' &&
21701                 !node.hasAttributes() &&
21702                 node.childNodes.length == 1 &&
21703                 node.firstChild.nodeName == "#text"  
21704         ) {
21705             var textNode = node.firstChild;
21706             node.removeChild(textNode);
21707             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21708                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21709             }
21710             node.parentNode.insertBefore(textNode, node);
21711             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21712                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21713             }
21714             node.parentNode.removeChild(node);
21715         }
21716         
21717         if (node.nodeName == "#text") {
21718             // clean up silly Windows -- stuff?
21719             return; 
21720         }
21721         if (node.nodeName == "#comment") {
21722             node.parentNode.removeChild(node);
21723             // clean up silly Windows -- stuff?
21724             return; 
21725         }
21726         
21727         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21728             node.parentNode.removeChild(node);
21729             return;
21730         }
21731         //Roo.log(node.tagName);
21732         // remove - but keep children..
21733         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21734             //Roo.log('-- removed');
21735             while (node.childNodes.length) {
21736                 var cn = node.childNodes[0];
21737                 node.removeChild(cn);
21738                 node.parentNode.insertBefore(cn, node);
21739                 // move node to parent - and clean it..
21740                 this.cleanWord(cn);
21741             }
21742             node.parentNode.removeChild(node);
21743             /// no need to iterate chidlren = it's got none..
21744             //this.iterateChildren(node, this.cleanWord);
21745             return;
21746         }
21747         // clean styles
21748         if (node.className.length) {
21749             
21750             var cn = node.className.split(/\W+/);
21751             var cna = [];
21752             Roo.each(cn, function(cls) {
21753                 if (cls.match(/Mso[a-zA-Z]+/)) {
21754                     return;
21755                 }
21756                 cna.push(cls);
21757             });
21758             node.className = cna.length ? cna.join(' ') : '';
21759             if (!cna.length) {
21760                 node.removeAttribute("class");
21761             }
21762         }
21763         
21764         if (node.hasAttribute("lang")) {
21765             node.removeAttribute("lang");
21766         }
21767         
21768         if (node.hasAttribute("style")) {
21769             
21770             var styles = node.getAttribute("style").split(";");
21771             var nstyle = [];
21772             Roo.each(styles, function(s) {
21773                 if (!s.match(/:/)) {
21774                     return;
21775                 }
21776                 var kv = s.split(":");
21777                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21778                     return;
21779                 }
21780                 // what ever is left... we allow.
21781                 nstyle.push(s);
21782             });
21783             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21784             if (!nstyle.length) {
21785                 node.removeAttribute('style');
21786             }
21787         }
21788         this.iterateChildren(node, this.cleanWord);
21789         
21790         
21791         
21792     },
21793     /**
21794      * iterateChildren of a Node, calling fn each time, using this as the scole..
21795      * @param {DomNode} node node to iterate children of.
21796      * @param {Function} fn method of this class to call on each item.
21797      */
21798     iterateChildren : function(node, fn)
21799     {
21800         if (!node.childNodes.length) {
21801                 return;
21802         }
21803         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21804            fn.call(this, node.childNodes[i])
21805         }
21806     },
21807     
21808     
21809     /**
21810      * cleanTableWidths.
21811      *
21812      * Quite often pasting from word etc.. results in tables with column and widths.
21813      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21814      *
21815      */
21816     cleanTableWidths : function(node)
21817     {
21818          
21819          
21820         if (!node) {
21821             this.cleanTableWidths(this.doc.body);
21822             return;
21823         }
21824         
21825         // ignore list...
21826         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21827             return; 
21828         }
21829         Roo.log(node.tagName);
21830         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21831             this.iterateChildren(node, this.cleanTableWidths);
21832             return;
21833         }
21834         if (node.hasAttribute('width')) {
21835             node.removeAttribute('width');
21836         }
21837         
21838          
21839         if (node.hasAttribute("style")) {
21840             // pretty basic...
21841             
21842             var styles = node.getAttribute("style").split(";");
21843             var nstyle = [];
21844             Roo.each(styles, function(s) {
21845                 if (!s.match(/:/)) {
21846                     return;
21847                 }
21848                 var kv = s.split(":");
21849                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21850                     return;
21851                 }
21852                 // what ever is left... we allow.
21853                 nstyle.push(s);
21854             });
21855             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21856             if (!nstyle.length) {
21857                 node.removeAttribute('style');
21858             }
21859         }
21860         
21861         this.iterateChildren(node, this.cleanTableWidths);
21862         
21863         
21864     },
21865     
21866     
21867     
21868     
21869     domToHTML : function(currentElement, depth, nopadtext) {
21870         
21871         depth = depth || 0;
21872         nopadtext = nopadtext || false;
21873     
21874         if (!currentElement) {
21875             return this.domToHTML(this.doc.body);
21876         }
21877         
21878         //Roo.log(currentElement);
21879         var j;
21880         var allText = false;
21881         var nodeName = currentElement.nodeName;
21882         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21883         
21884         if  (nodeName == '#text') {
21885             
21886             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21887         }
21888         
21889         
21890         var ret = '';
21891         if (nodeName != 'BODY') {
21892              
21893             var i = 0;
21894             // Prints the node tagName, such as <A>, <IMG>, etc
21895             if (tagName) {
21896                 var attr = [];
21897                 for(i = 0; i < currentElement.attributes.length;i++) {
21898                     // quoting?
21899                     var aname = currentElement.attributes.item(i).name;
21900                     if (!currentElement.attributes.item(i).value.length) {
21901                         continue;
21902                     }
21903                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21904                 }
21905                 
21906                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21907             } 
21908             else {
21909                 
21910                 // eack
21911             }
21912         } else {
21913             tagName = false;
21914         }
21915         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21916             return ret;
21917         }
21918         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21919             nopadtext = true;
21920         }
21921         
21922         
21923         // Traverse the tree
21924         i = 0;
21925         var currentElementChild = currentElement.childNodes.item(i);
21926         var allText = true;
21927         var innerHTML  = '';
21928         lastnode = '';
21929         while (currentElementChild) {
21930             // Formatting code (indent the tree so it looks nice on the screen)
21931             var nopad = nopadtext;
21932             if (lastnode == 'SPAN') {
21933                 nopad  = true;
21934             }
21935             // text
21936             if  (currentElementChild.nodeName == '#text') {
21937                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21938                 toadd = nopadtext ? toadd : toadd.trim();
21939                 if (!nopad && toadd.length > 80) {
21940                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21941                 }
21942                 innerHTML  += toadd;
21943                 
21944                 i++;
21945                 currentElementChild = currentElement.childNodes.item(i);
21946                 lastNode = '';
21947                 continue;
21948             }
21949             allText = false;
21950             
21951             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21952                 
21953             // Recursively traverse the tree structure of the child node
21954             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21955             lastnode = currentElementChild.nodeName;
21956             i++;
21957             currentElementChild=currentElement.childNodes.item(i);
21958         }
21959         
21960         ret += innerHTML;
21961         
21962         if (!allText) {
21963                 // The remaining code is mostly for formatting the tree
21964             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21965         }
21966         
21967         
21968         if (tagName) {
21969             ret+= "</"+tagName+">";
21970         }
21971         return ret;
21972         
21973     },
21974         
21975     applyBlacklists : function()
21976     {
21977         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21978         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21979         
21980         this.white = [];
21981         this.black = [];
21982         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21983             if (b.indexOf(tag) > -1) {
21984                 return;
21985             }
21986             this.white.push(tag);
21987             
21988         }, this);
21989         
21990         Roo.each(w, function(tag) {
21991             if (b.indexOf(tag) > -1) {
21992                 return;
21993             }
21994             if (this.white.indexOf(tag) > -1) {
21995                 return;
21996             }
21997             this.white.push(tag);
21998             
21999         }, this);
22000         
22001         
22002         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22003             if (w.indexOf(tag) > -1) {
22004                 return;
22005             }
22006             this.black.push(tag);
22007             
22008         }, this);
22009         
22010         Roo.each(b, function(tag) {
22011             if (w.indexOf(tag) > -1) {
22012                 return;
22013             }
22014             if (this.black.indexOf(tag) > -1) {
22015                 return;
22016             }
22017             this.black.push(tag);
22018             
22019         }, this);
22020         
22021         
22022         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22023         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22024         
22025         this.cwhite = [];
22026         this.cblack = [];
22027         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22028             if (b.indexOf(tag) > -1) {
22029                 return;
22030             }
22031             this.cwhite.push(tag);
22032             
22033         }, this);
22034         
22035         Roo.each(w, function(tag) {
22036             if (b.indexOf(tag) > -1) {
22037                 return;
22038             }
22039             if (this.cwhite.indexOf(tag) > -1) {
22040                 return;
22041             }
22042             this.cwhite.push(tag);
22043             
22044         }, this);
22045         
22046         
22047         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22048             if (w.indexOf(tag) > -1) {
22049                 return;
22050             }
22051             this.cblack.push(tag);
22052             
22053         }, this);
22054         
22055         Roo.each(b, function(tag) {
22056             if (w.indexOf(tag) > -1) {
22057                 return;
22058             }
22059             if (this.cblack.indexOf(tag) > -1) {
22060                 return;
22061             }
22062             this.cblack.push(tag);
22063             
22064         }, this);
22065     },
22066     
22067     setStylesheets : function(stylesheets)
22068     {
22069         if(typeof(stylesheets) == 'string'){
22070             Roo.get(this.iframe.contentDocument.head).createChild({
22071                 tag : 'link',
22072                 rel : 'stylesheet',
22073                 type : 'text/css',
22074                 href : stylesheets
22075             });
22076             
22077             return;
22078         }
22079         var _this = this;
22080      
22081         Roo.each(stylesheets, function(s) {
22082             if(!s.length){
22083                 return;
22084             }
22085             
22086             Roo.get(_this.iframe.contentDocument.head).createChild({
22087                 tag : 'link',
22088                 rel : 'stylesheet',
22089                 type : 'text/css',
22090                 href : s
22091             });
22092         });
22093
22094         
22095     },
22096     
22097     removeStylesheets : function()
22098     {
22099         var _this = this;
22100         
22101         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22102             s.remove();
22103         });
22104     },
22105     
22106     setStyle : function(style)
22107     {
22108         Roo.get(this.iframe.contentDocument.head).createChild({
22109             tag : 'style',
22110             type : 'text/css',
22111             html : style
22112         });
22113
22114         return;
22115     }
22116     
22117     // hide stuff that is not compatible
22118     /**
22119      * @event blur
22120      * @hide
22121      */
22122     /**
22123      * @event change
22124      * @hide
22125      */
22126     /**
22127      * @event focus
22128      * @hide
22129      */
22130     /**
22131      * @event specialkey
22132      * @hide
22133      */
22134     /**
22135      * @cfg {String} fieldClass @hide
22136      */
22137     /**
22138      * @cfg {String} focusClass @hide
22139      */
22140     /**
22141      * @cfg {String} autoCreate @hide
22142      */
22143     /**
22144      * @cfg {String} inputType @hide
22145      */
22146     /**
22147      * @cfg {String} invalidClass @hide
22148      */
22149     /**
22150      * @cfg {String} invalidText @hide
22151      */
22152     /**
22153      * @cfg {String} msgFx @hide
22154      */
22155     /**
22156      * @cfg {String} validateOnBlur @hide
22157      */
22158 });
22159
22160 Roo.HtmlEditorCore.white = [
22161         'area', 'br', 'img', 'input', 'hr', 'wbr',
22162         
22163        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22164        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22165        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22166        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22167        'table',   'ul',         'xmp', 
22168        
22169        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22170       'thead',   'tr', 
22171      
22172       'dir', 'menu', 'ol', 'ul', 'dl',
22173        
22174       'embed',  'object'
22175 ];
22176
22177
22178 Roo.HtmlEditorCore.black = [
22179     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22180         'applet', // 
22181         'base',   'basefont', 'bgsound', 'blink',  'body', 
22182         'frame',  'frameset', 'head',    'html',   'ilayer', 
22183         'iframe', 'layer',  'link',     'meta',    'object',   
22184         'script', 'style' ,'title',  'xml' // clean later..
22185 ];
22186 Roo.HtmlEditorCore.clean = [
22187     'script', 'style', 'title', 'xml'
22188 ];
22189 Roo.HtmlEditorCore.remove = [
22190     'font'
22191 ];
22192 // attributes..
22193
22194 Roo.HtmlEditorCore.ablack = [
22195     'on'
22196 ];
22197     
22198 Roo.HtmlEditorCore.aclean = [ 
22199     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22200 ];
22201
22202 // protocols..
22203 Roo.HtmlEditorCore.pwhite= [
22204         'http',  'https',  'mailto'
22205 ];
22206
22207 // white listed style attributes.
22208 Roo.HtmlEditorCore.cwhite= [
22209       //  'text-align', /// default is to allow most things..
22210       
22211          
22212 //        'font-size'//??
22213 ];
22214
22215 // black listed style attributes.
22216 Roo.HtmlEditorCore.cblack= [
22217       //  'font-size' -- this can be set by the project 
22218 ];
22219
22220
22221 Roo.HtmlEditorCore.swapCodes   =[ 
22222     [    8211, "--" ], 
22223     [    8212, "--" ], 
22224     [    8216,  "'" ],  
22225     [    8217, "'" ],  
22226     [    8220, '"' ],  
22227     [    8221, '"' ],  
22228     [    8226, "*" ],  
22229     [    8230, "..." ]
22230 ]; 
22231
22232     //<script type="text/javascript">
22233
22234 /*
22235  * Ext JS Library 1.1.1
22236  * Copyright(c) 2006-2007, Ext JS, LLC.
22237  * Licence LGPL
22238  * 
22239  */
22240  
22241  
22242 Roo.form.HtmlEditor = function(config){
22243     
22244     
22245     
22246     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22247     
22248     if (!this.toolbars) {
22249         this.toolbars = [];
22250     }
22251     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22252     
22253     
22254 };
22255
22256 /**
22257  * @class Roo.form.HtmlEditor
22258  * @extends Roo.form.Field
22259  * Provides a lightweight HTML Editor component.
22260  *
22261  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22262  * 
22263  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22264  * supported by this editor.</b><br/><br/>
22265  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22266  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22267  */
22268 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22269     /**
22270      * @cfg {Boolean} clearUp
22271      */
22272     clearUp : true,
22273       /**
22274      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22275      */
22276     toolbars : false,
22277    
22278      /**
22279      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22280      *                        Roo.resizable.
22281      */
22282     resizable : false,
22283      /**
22284      * @cfg {Number} height (in pixels)
22285      */   
22286     height: 300,
22287    /**
22288      * @cfg {Number} width (in pixels)
22289      */   
22290     width: 500,
22291     
22292     /**
22293      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22294      * 
22295      */
22296     stylesheets: false,
22297     
22298     
22299      /**
22300      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22301      * 
22302      */
22303     cblack: false,
22304     /**
22305      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22306      * 
22307      */
22308     cwhite: false,
22309     
22310      /**
22311      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22312      * 
22313      */
22314     black: false,
22315     /**
22316      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22317      * 
22318      */
22319     white: false,
22320     
22321     // id of frame..
22322     frameId: false,
22323     
22324     // private properties
22325     validationEvent : false,
22326     deferHeight: true,
22327     initialized : false,
22328     activated : false,
22329     
22330     onFocus : Roo.emptyFn,
22331     iframePad:3,
22332     hideMode:'offsets',
22333     
22334     actionMode : 'container', // defaults to hiding it...
22335     
22336     defaultAutoCreate : { // modified by initCompnoent..
22337         tag: "textarea",
22338         style:"width:500px;height:300px;",
22339         autocomplete: "new-password"
22340     },
22341
22342     // private
22343     initComponent : function(){
22344         this.addEvents({
22345             /**
22346              * @event initialize
22347              * Fires when the editor is fully initialized (including the iframe)
22348              * @param {HtmlEditor} this
22349              */
22350             initialize: true,
22351             /**
22352              * @event activate
22353              * Fires when the editor is first receives the focus. Any insertion must wait
22354              * until after this event.
22355              * @param {HtmlEditor} this
22356              */
22357             activate: true,
22358              /**
22359              * @event beforesync
22360              * Fires before the textarea is updated with content from the editor iframe. Return false
22361              * to cancel the sync.
22362              * @param {HtmlEditor} this
22363              * @param {String} html
22364              */
22365             beforesync: true,
22366              /**
22367              * @event beforepush
22368              * Fires before the iframe editor is updated with content from the textarea. Return false
22369              * to cancel the push.
22370              * @param {HtmlEditor} this
22371              * @param {String} html
22372              */
22373             beforepush: true,
22374              /**
22375              * @event sync
22376              * Fires when the textarea is updated with content from the editor iframe.
22377              * @param {HtmlEditor} this
22378              * @param {String} html
22379              */
22380             sync: true,
22381              /**
22382              * @event push
22383              * Fires when the iframe editor is updated with content from the textarea.
22384              * @param {HtmlEditor} this
22385              * @param {String} html
22386              */
22387             push: true,
22388              /**
22389              * @event editmodechange
22390              * Fires when the editor switches edit modes
22391              * @param {HtmlEditor} this
22392              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22393              */
22394             editmodechange: true,
22395             /**
22396              * @event editorevent
22397              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22398              * @param {HtmlEditor} this
22399              */
22400             editorevent: true,
22401             /**
22402              * @event firstfocus
22403              * Fires when on first focus - needed by toolbars..
22404              * @param {HtmlEditor} this
22405              */
22406             firstfocus: true,
22407             /**
22408              * @event autosave
22409              * Auto save the htmlEditor value as a file into Events
22410              * @param {HtmlEditor} this
22411              */
22412             autosave: true,
22413             /**
22414              * @event savedpreview
22415              * preview the saved version of htmlEditor
22416              * @param {HtmlEditor} this
22417              */
22418             savedpreview: true,
22419             
22420             /**
22421             * @event stylesheetsclick
22422             * Fires when press the Sytlesheets button
22423             * @param {Roo.HtmlEditorCore} this
22424             */
22425             stylesheetsclick: true
22426         });
22427         this.defaultAutoCreate =  {
22428             tag: "textarea",
22429             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22430             autocomplete: "new-password"
22431         };
22432     },
22433
22434     /**
22435      * Protected method that will not generally be called directly. It
22436      * is called when the editor creates its toolbar. Override this method if you need to
22437      * add custom toolbar buttons.
22438      * @param {HtmlEditor} editor
22439      */
22440     createToolbar : function(editor){
22441         Roo.log("create toolbars");
22442         if (!editor.toolbars || !editor.toolbars.length) {
22443             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22444         }
22445         
22446         for (var i =0 ; i < editor.toolbars.length;i++) {
22447             editor.toolbars[i] = Roo.factory(
22448                     typeof(editor.toolbars[i]) == 'string' ?
22449                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22450                 Roo.form.HtmlEditor);
22451             editor.toolbars[i].init(editor);
22452         }
22453          
22454         
22455     },
22456
22457      
22458     // private
22459     onRender : function(ct, position)
22460     {
22461         var _t = this;
22462         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22463         
22464         this.wrap = this.el.wrap({
22465             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22466         });
22467         
22468         this.editorcore.onRender(ct, position);
22469          
22470         if (this.resizable) {
22471             this.resizeEl = new Roo.Resizable(this.wrap, {
22472                 pinned : true,
22473                 wrap: true,
22474                 dynamic : true,
22475                 minHeight : this.height,
22476                 height: this.height,
22477                 handles : this.resizable,
22478                 width: this.width,
22479                 listeners : {
22480                     resize : function(r, w, h) {
22481                         _t.onResize(w,h); // -something
22482                     }
22483                 }
22484             });
22485             
22486         }
22487         this.createToolbar(this);
22488        
22489         
22490         if(!this.width){
22491             this.setSize(this.wrap.getSize());
22492         }
22493         if (this.resizeEl) {
22494             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22495             // should trigger onReize..
22496         }
22497         
22498         this.keyNav = new Roo.KeyNav(this.el, {
22499             
22500             "tab" : function(e){
22501                 e.preventDefault();
22502                 
22503                 var value = this.getValue();
22504                 
22505                 var start = this.el.dom.selectionStart;
22506                 var end = this.el.dom.selectionEnd;
22507                 
22508                 if(!e.shiftKey){
22509                     
22510                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22511                     this.el.dom.setSelectionRange(end + 1, end + 1);
22512                     return;
22513                 }
22514                 
22515                 var f = value.substring(0, start).split("\t");
22516                 
22517                 if(f.pop().length != 0){
22518                     return;
22519                 }
22520                 
22521                 this.setValue(f.join("\t") + value.substring(end));
22522                 this.el.dom.setSelectionRange(start - 1, start - 1);
22523                 
22524             },
22525             
22526             "home" : function(e){
22527                 e.preventDefault();
22528                 
22529                 var curr = this.el.dom.selectionStart;
22530                 var lines = this.getValue().split("\n");
22531                 
22532                 if(!lines.length){
22533                     return;
22534                 }
22535                 
22536                 if(e.ctrlKey){
22537                     this.el.dom.setSelectionRange(0, 0);
22538                     return;
22539                 }
22540                 
22541                 var pos = 0;
22542                 
22543                 for (var i = 0; i < lines.length;i++) {
22544                     pos += lines[i].length;
22545                     
22546                     if(i != 0){
22547                         pos += 1;
22548                     }
22549                     
22550                     if(pos < curr){
22551                         continue;
22552                     }
22553                     
22554                     pos -= lines[i].length;
22555                     
22556                     break;
22557                 }
22558                 
22559                 if(!e.shiftKey){
22560                     this.el.dom.setSelectionRange(pos, pos);
22561                     return;
22562                 }
22563                 
22564                 this.el.dom.selectionStart = pos;
22565                 this.el.dom.selectionEnd = curr;
22566             },
22567             
22568             "end" : function(e){
22569                 e.preventDefault();
22570                 
22571                 var curr = this.el.dom.selectionStart;
22572                 var lines = this.getValue().split("\n");
22573                 
22574                 if(!lines.length){
22575                     return;
22576                 }
22577                 
22578                 if(e.ctrlKey){
22579                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22580                     return;
22581                 }
22582                 
22583                 var pos = 0;
22584                 
22585                 for (var i = 0; i < lines.length;i++) {
22586                     
22587                     pos += lines[i].length;
22588                     
22589                     if(i != 0){
22590                         pos += 1;
22591                     }
22592                     
22593                     if(pos < curr){
22594                         continue;
22595                     }
22596                     
22597                     break;
22598                 }
22599                 
22600                 if(!e.shiftKey){
22601                     this.el.dom.setSelectionRange(pos, pos);
22602                     return;
22603                 }
22604                 
22605                 this.el.dom.selectionStart = curr;
22606                 this.el.dom.selectionEnd = pos;
22607             },
22608
22609             scope : this,
22610
22611             doRelay : function(foo, bar, hname){
22612                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22613             },
22614
22615             forceKeyDown: true
22616         });
22617         
22618 //        if(this.autosave && this.w){
22619 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22620 //        }
22621     },
22622
22623     // private
22624     onResize : function(w, h)
22625     {
22626         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22627         var ew = false;
22628         var eh = false;
22629         
22630         if(this.el ){
22631             if(typeof w == 'number'){
22632                 var aw = w - this.wrap.getFrameWidth('lr');
22633                 this.el.setWidth(this.adjustWidth('textarea', aw));
22634                 ew = aw;
22635             }
22636             if(typeof h == 'number'){
22637                 var tbh = 0;
22638                 for (var i =0; i < this.toolbars.length;i++) {
22639                     // fixme - ask toolbars for heights?
22640                     tbh += this.toolbars[i].tb.el.getHeight();
22641                     if (this.toolbars[i].footer) {
22642                         tbh += this.toolbars[i].footer.el.getHeight();
22643                     }
22644                 }
22645                 
22646                 
22647                 
22648                 
22649                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22650                 ah -= 5; // knock a few pixes off for look..
22651 //                Roo.log(ah);
22652                 this.el.setHeight(this.adjustWidth('textarea', ah));
22653                 var eh = ah;
22654             }
22655         }
22656         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22657         this.editorcore.onResize(ew,eh);
22658         
22659     },
22660
22661     /**
22662      * Toggles the editor between standard and source edit mode.
22663      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22664      */
22665     toggleSourceEdit : function(sourceEditMode)
22666     {
22667         this.editorcore.toggleSourceEdit(sourceEditMode);
22668         
22669         if(this.editorcore.sourceEditMode){
22670             Roo.log('editor - showing textarea');
22671             
22672 //            Roo.log('in');
22673 //            Roo.log(this.syncValue());
22674             this.editorcore.syncValue();
22675             this.el.removeClass('x-hidden');
22676             this.el.dom.removeAttribute('tabIndex');
22677             this.el.focus();
22678             
22679             for (var i = 0; i < this.toolbars.length; i++) {
22680                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22681                     this.toolbars[i].tb.hide();
22682                     this.toolbars[i].footer.hide();
22683                 }
22684             }
22685             
22686         }else{
22687             Roo.log('editor - hiding textarea');
22688 //            Roo.log('out')
22689 //            Roo.log(this.pushValue()); 
22690             this.editorcore.pushValue();
22691             
22692             this.el.addClass('x-hidden');
22693             this.el.dom.setAttribute('tabIndex', -1);
22694             
22695             for (var i = 0; i < this.toolbars.length; i++) {
22696                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22697                     this.toolbars[i].tb.show();
22698                     this.toolbars[i].footer.show();
22699                 }
22700             }
22701             
22702             //this.deferFocus();
22703         }
22704         
22705         this.setSize(this.wrap.getSize());
22706         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22707         
22708         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22709     },
22710  
22711     // private (for BoxComponent)
22712     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22713
22714     // private (for BoxComponent)
22715     getResizeEl : function(){
22716         return this.wrap;
22717     },
22718
22719     // private (for BoxComponent)
22720     getPositionEl : function(){
22721         return this.wrap;
22722     },
22723
22724     // private
22725     initEvents : function(){
22726         this.originalValue = this.getValue();
22727     },
22728
22729     /**
22730      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22731      * @method
22732      */
22733     markInvalid : Roo.emptyFn,
22734     /**
22735      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22736      * @method
22737      */
22738     clearInvalid : Roo.emptyFn,
22739
22740     setValue : function(v){
22741         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22742         this.editorcore.pushValue();
22743     },
22744
22745      
22746     // private
22747     deferFocus : function(){
22748         this.focus.defer(10, this);
22749     },
22750
22751     // doc'ed in Field
22752     focus : function(){
22753         this.editorcore.focus();
22754         
22755     },
22756       
22757
22758     // private
22759     onDestroy : function(){
22760         
22761         
22762         
22763         if(this.rendered){
22764             
22765             for (var i =0; i < this.toolbars.length;i++) {
22766                 // fixme - ask toolbars for heights?
22767                 this.toolbars[i].onDestroy();
22768             }
22769             
22770             this.wrap.dom.innerHTML = '';
22771             this.wrap.remove();
22772         }
22773     },
22774
22775     // private
22776     onFirstFocus : function(){
22777         //Roo.log("onFirstFocus");
22778         this.editorcore.onFirstFocus();
22779          for (var i =0; i < this.toolbars.length;i++) {
22780             this.toolbars[i].onFirstFocus();
22781         }
22782         
22783     },
22784     
22785     // private
22786     syncValue : function()
22787     {
22788         this.editorcore.syncValue();
22789     },
22790     
22791     pushValue : function()
22792     {
22793         this.editorcore.pushValue();
22794     },
22795     
22796     setStylesheets : function(stylesheets)
22797     {
22798         this.editorcore.setStylesheets(stylesheets);
22799     },
22800     
22801     removeStylesheets : function()
22802     {
22803         this.editorcore.removeStylesheets();
22804     }
22805      
22806     
22807     // hide stuff that is not compatible
22808     /**
22809      * @event blur
22810      * @hide
22811      */
22812     /**
22813      * @event change
22814      * @hide
22815      */
22816     /**
22817      * @event focus
22818      * @hide
22819      */
22820     /**
22821      * @event specialkey
22822      * @hide
22823      */
22824     /**
22825      * @cfg {String} fieldClass @hide
22826      */
22827     /**
22828      * @cfg {String} focusClass @hide
22829      */
22830     /**
22831      * @cfg {String} autoCreate @hide
22832      */
22833     /**
22834      * @cfg {String} inputType @hide
22835      */
22836     /**
22837      * @cfg {String} invalidClass @hide
22838      */
22839     /**
22840      * @cfg {String} invalidText @hide
22841      */
22842     /**
22843      * @cfg {String} msgFx @hide
22844      */
22845     /**
22846      * @cfg {String} validateOnBlur @hide
22847      */
22848 });
22849  
22850     // <script type="text/javascript">
22851 /*
22852  * Based on
22853  * Ext JS Library 1.1.1
22854  * Copyright(c) 2006-2007, Ext JS, LLC.
22855  *  
22856  
22857  */
22858
22859 /**
22860  * @class Roo.form.HtmlEditorToolbar1
22861  * Basic Toolbar
22862  * 
22863  * Usage:
22864  *
22865  new Roo.form.HtmlEditor({
22866     ....
22867     toolbars : [
22868         new Roo.form.HtmlEditorToolbar1({
22869             disable : { fonts: 1 , format: 1, ..., ... , ...],
22870             btns : [ .... ]
22871         })
22872     }
22873      
22874  * 
22875  * @cfg {Object} disable List of elements to disable..
22876  * @cfg {Array} btns List of additional buttons.
22877  * 
22878  * 
22879  * NEEDS Extra CSS? 
22880  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22881  */
22882  
22883 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22884 {
22885     
22886     Roo.apply(this, config);
22887     
22888     // default disabled, based on 'good practice'..
22889     this.disable = this.disable || {};
22890     Roo.applyIf(this.disable, {
22891         fontSize : true,
22892         colors : true,
22893         specialElements : true
22894     });
22895     
22896     
22897     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22898     // dont call parent... till later.
22899 }
22900
22901 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22902     
22903     tb: false,
22904     
22905     rendered: false,
22906     
22907     editor : false,
22908     editorcore : false,
22909     /**
22910      * @cfg {Object} disable  List of toolbar elements to disable
22911          
22912      */
22913     disable : false,
22914     
22915     
22916      /**
22917      * @cfg {String} createLinkText The default text for the create link prompt
22918      */
22919     createLinkText : 'Please enter the URL for the link:',
22920     /**
22921      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22922      */
22923     defaultLinkValue : 'http:/'+'/',
22924    
22925     
22926       /**
22927      * @cfg {Array} fontFamilies An array of available font families
22928      */
22929     fontFamilies : [
22930         'Arial',
22931         'Courier New',
22932         'Tahoma',
22933         'Times New Roman',
22934         'Verdana'
22935     ],
22936     
22937     specialChars : [
22938            "&#169;",
22939           "&#174;",     
22940           "&#8482;",    
22941           "&#163;" ,    
22942          // "&#8212;",    
22943           "&#8230;",    
22944           "&#247;" ,    
22945         //  "&#225;" ,     ?? a acute?
22946            "&#8364;"    , //Euro
22947        //   "&#8220;"    ,
22948         //  "&#8221;"    ,
22949         //  "&#8226;"    ,
22950           "&#176;"  //   , // degrees
22951
22952          // "&#233;"     , // e ecute
22953          // "&#250;"     , // u ecute?
22954     ],
22955     
22956     specialElements : [
22957         {
22958             text: "Insert Table",
22959             xtype: 'MenuItem',
22960             xns : Roo.Menu,
22961             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22962                 
22963         },
22964         {    
22965             text: "Insert Image",
22966             xtype: 'MenuItem',
22967             xns : Roo.Menu,
22968             ihtml : '<img src="about:blank"/>'
22969             
22970         }
22971         
22972          
22973     ],
22974     
22975     
22976     inputElements : [ 
22977             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22978             "input:submit", "input:button", "select", "textarea", "label" ],
22979     formats : [
22980         ["p"] ,  
22981         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22982         ["pre"],[ "code"], 
22983         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22984         ['div'],['span'],
22985         ['sup'],['sub']
22986     ],
22987     
22988     cleanStyles : [
22989         "font-size"
22990     ],
22991      /**
22992      * @cfg {String} defaultFont default font to use.
22993      */
22994     defaultFont: 'tahoma',
22995    
22996     fontSelect : false,
22997     
22998     
22999     formatCombo : false,
23000     
23001     init : function(editor)
23002     {
23003         this.editor = editor;
23004         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23005         var editorcore = this.editorcore;
23006         
23007         var _t = this;
23008         
23009         var fid = editorcore.frameId;
23010         var etb = this;
23011         function btn(id, toggle, handler){
23012             var xid = fid + '-'+ id ;
23013             return {
23014                 id : xid,
23015                 cmd : id,
23016                 cls : 'x-btn-icon x-edit-'+id,
23017                 enableToggle:toggle !== false,
23018                 scope: _t, // was editor...
23019                 handler:handler||_t.relayBtnCmd,
23020                 clickEvent:'mousedown',
23021                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23022                 tabIndex:-1
23023             };
23024         }
23025         
23026         
23027         
23028         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23029         this.tb = tb;
23030          // stop form submits
23031         tb.el.on('click', function(e){
23032             e.preventDefault(); // what does this do?
23033         });
23034
23035         if(!this.disable.font) { // && !Roo.isSafari){
23036             /* why no safari for fonts 
23037             editor.fontSelect = tb.el.createChild({
23038                 tag:'select',
23039                 tabIndex: -1,
23040                 cls:'x-font-select',
23041                 html: this.createFontOptions()
23042             });
23043             
23044             editor.fontSelect.on('change', function(){
23045                 var font = editor.fontSelect.dom.value;
23046                 editor.relayCmd('fontname', font);
23047                 editor.deferFocus();
23048             }, editor);
23049             
23050             tb.add(
23051                 editor.fontSelect.dom,
23052                 '-'
23053             );
23054             */
23055             
23056         };
23057         if(!this.disable.formats){
23058             this.formatCombo = new Roo.form.ComboBox({
23059                 store: new Roo.data.SimpleStore({
23060                     id : 'tag',
23061                     fields: ['tag'],
23062                     data : this.formats // from states.js
23063                 }),
23064                 blockFocus : true,
23065                 name : '',
23066                 //autoCreate : {tag: "div",  size: "20"},
23067                 displayField:'tag',
23068                 typeAhead: false,
23069                 mode: 'local',
23070                 editable : false,
23071                 triggerAction: 'all',
23072                 emptyText:'Add tag',
23073                 selectOnFocus:true,
23074                 width:135,
23075                 listeners : {
23076                     'select': function(c, r, i) {
23077                         editorcore.insertTag(r.get('tag'));
23078                         editor.focus();
23079                     }
23080                 }
23081
23082             });
23083             tb.addField(this.formatCombo);
23084             
23085         }
23086         
23087         if(!this.disable.format){
23088             tb.add(
23089                 btn('bold'),
23090                 btn('italic'),
23091                 btn('underline'),
23092                 btn('strikethrough')
23093             );
23094         };
23095         if(!this.disable.fontSize){
23096             tb.add(
23097                 '-',
23098                 
23099                 
23100                 btn('increasefontsize', false, editorcore.adjustFont),
23101                 btn('decreasefontsize', false, editorcore.adjustFont)
23102             );
23103         };
23104         
23105         
23106         if(!this.disable.colors){
23107             tb.add(
23108                 '-', {
23109                     id:editorcore.frameId +'-forecolor',
23110                     cls:'x-btn-icon x-edit-forecolor',
23111                     clickEvent:'mousedown',
23112                     tooltip: this.buttonTips['forecolor'] || undefined,
23113                     tabIndex:-1,
23114                     menu : new Roo.menu.ColorMenu({
23115                         allowReselect: true,
23116                         focus: Roo.emptyFn,
23117                         value:'000000',
23118                         plain:true,
23119                         selectHandler: function(cp, color){
23120                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23121                             editor.deferFocus();
23122                         },
23123                         scope: editorcore,
23124                         clickEvent:'mousedown'
23125                     })
23126                 }, {
23127                     id:editorcore.frameId +'backcolor',
23128                     cls:'x-btn-icon x-edit-backcolor',
23129                     clickEvent:'mousedown',
23130                     tooltip: this.buttonTips['backcolor'] || undefined,
23131                     tabIndex:-1,
23132                     menu : new Roo.menu.ColorMenu({
23133                         focus: Roo.emptyFn,
23134                         value:'FFFFFF',
23135                         plain:true,
23136                         allowReselect: true,
23137                         selectHandler: function(cp, color){
23138                             if(Roo.isGecko){
23139                                 editorcore.execCmd('useCSS', false);
23140                                 editorcore.execCmd('hilitecolor', color);
23141                                 editorcore.execCmd('useCSS', true);
23142                                 editor.deferFocus();
23143                             }else{
23144                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23145                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23146                                 editor.deferFocus();
23147                             }
23148                         },
23149                         scope:editorcore,
23150                         clickEvent:'mousedown'
23151                     })
23152                 }
23153             );
23154         };
23155         // now add all the items...
23156         
23157
23158         if(!this.disable.alignments){
23159             tb.add(
23160                 '-',
23161                 btn('justifyleft'),
23162                 btn('justifycenter'),
23163                 btn('justifyright')
23164             );
23165         };
23166
23167         //if(!Roo.isSafari){
23168             if(!this.disable.links){
23169                 tb.add(
23170                     '-',
23171                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23172                 );
23173             };
23174
23175             if(!this.disable.lists){
23176                 tb.add(
23177                     '-',
23178                     btn('insertorderedlist'),
23179                     btn('insertunorderedlist')
23180                 );
23181             }
23182             if(!this.disable.sourceEdit){
23183                 tb.add(
23184                     '-',
23185                     btn('sourceedit', true, function(btn){
23186                         this.toggleSourceEdit(btn.pressed);
23187                     })
23188                 );
23189             }
23190         //}
23191         
23192         var smenu = { };
23193         // special menu.. - needs to be tidied up..
23194         if (!this.disable.special) {
23195             smenu = {
23196                 text: "&#169;",
23197                 cls: 'x-edit-none',
23198                 
23199                 menu : {
23200                     items : []
23201                 }
23202             };
23203             for (var i =0; i < this.specialChars.length; i++) {
23204                 smenu.menu.items.push({
23205                     
23206                     html: this.specialChars[i],
23207                     handler: function(a,b) {
23208                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23209                         //editor.insertAtCursor(a.html);
23210                         
23211                     },
23212                     tabIndex:-1
23213                 });
23214             }
23215             
23216             
23217             tb.add(smenu);
23218             
23219             
23220         }
23221         
23222         var cmenu = { };
23223         if (!this.disable.cleanStyles) {
23224             cmenu = {
23225                 cls: 'x-btn-icon x-btn-clear',
23226                 
23227                 menu : {
23228                     items : []
23229                 }
23230             };
23231             for (var i =0; i < this.cleanStyles.length; i++) {
23232                 cmenu.menu.items.push({
23233                     actiontype : this.cleanStyles[i],
23234                     html: 'Remove ' + this.cleanStyles[i],
23235                     handler: function(a,b) {
23236 //                        Roo.log(a);
23237 //                        Roo.log(b);
23238                         var c = Roo.get(editorcore.doc.body);
23239                         c.select('[style]').each(function(s) {
23240                             s.dom.style.removeProperty(a.actiontype);
23241                         });
23242                         editorcore.syncValue();
23243                     },
23244                     tabIndex:-1
23245                 });
23246             }
23247              cmenu.menu.items.push({
23248                 actiontype : 'tablewidths',
23249                 html: 'Remove Table Widths',
23250                 handler: function(a,b) {
23251                     editorcore.cleanTableWidths();
23252                     editorcore.syncValue();
23253                 },
23254                 tabIndex:-1
23255             });
23256             cmenu.menu.items.push({
23257                 actiontype : 'word',
23258                 html: 'Remove MS Word Formating',
23259                 handler: function(a,b) {
23260                     editorcore.cleanWord();
23261                     editorcore.syncValue();
23262                 },
23263                 tabIndex:-1
23264             });
23265             
23266             cmenu.menu.items.push({
23267                 actiontype : 'all',
23268                 html: 'Remove All Styles',
23269                 handler: function(a,b) {
23270                     
23271                     var c = Roo.get(editorcore.doc.body);
23272                     c.select('[style]').each(function(s) {
23273                         s.dom.removeAttribute('style');
23274                     });
23275                     editorcore.syncValue();
23276                 },
23277                 tabIndex:-1
23278             });
23279             
23280             cmenu.menu.items.push({
23281                 actiontype : 'all',
23282                 html: 'Remove All CSS Classes',
23283                 handler: function(a,b) {
23284                     
23285                     var c = Roo.get(editorcore.doc.body);
23286                     c.select('[class]').each(function(s) {
23287                         s.dom.removeAttribute('class');
23288                     });
23289                     editorcore.cleanWord();
23290                     editorcore.syncValue();
23291                 },
23292                 tabIndex:-1
23293             });
23294             
23295              cmenu.menu.items.push({
23296                 actiontype : 'tidy',
23297                 html: 'Tidy HTML Source',
23298                 handler: function(a,b) {
23299                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23300                     editorcore.syncValue();
23301                 },
23302                 tabIndex:-1
23303             });
23304             
23305             
23306             tb.add(cmenu);
23307         }
23308          
23309         if (!this.disable.specialElements) {
23310             var semenu = {
23311                 text: "Other;",
23312                 cls: 'x-edit-none',
23313                 menu : {
23314                     items : []
23315                 }
23316             };
23317             for (var i =0; i < this.specialElements.length; i++) {
23318                 semenu.menu.items.push(
23319                     Roo.apply({ 
23320                         handler: function(a,b) {
23321                             editor.insertAtCursor(this.ihtml);
23322                         }
23323                     }, this.specialElements[i])
23324                 );
23325                     
23326             }
23327             
23328             tb.add(semenu);
23329             
23330             
23331         }
23332          
23333         
23334         if (this.btns) {
23335             for(var i =0; i< this.btns.length;i++) {
23336                 var b = Roo.factory(this.btns[i],Roo.form);
23337                 b.cls =  'x-edit-none';
23338                 
23339                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23340                     b.cls += ' x-init-enable';
23341                 }
23342                 
23343                 b.scope = editorcore;
23344                 tb.add(b);
23345             }
23346         
23347         }
23348         
23349         
23350         
23351         // disable everything...
23352         
23353         this.tb.items.each(function(item){
23354             
23355            if(
23356                 item.id != editorcore.frameId+ '-sourceedit' && 
23357                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23358             ){
23359                 
23360                 item.disable();
23361             }
23362         });
23363         this.rendered = true;
23364         
23365         // the all the btns;
23366         editor.on('editorevent', this.updateToolbar, this);
23367         // other toolbars need to implement this..
23368         //editor.on('editmodechange', this.updateToolbar, this);
23369     },
23370     
23371     
23372     relayBtnCmd : function(btn) {
23373         this.editorcore.relayCmd(btn.cmd);
23374     },
23375     // private used internally
23376     createLink : function(){
23377         Roo.log("create link?");
23378         var url = prompt(this.createLinkText, this.defaultLinkValue);
23379         if(url && url != 'http:/'+'/'){
23380             this.editorcore.relayCmd('createlink', url);
23381         }
23382     },
23383
23384     
23385     /**
23386      * Protected method that will not generally be called directly. It triggers
23387      * a toolbar update by reading the markup state of the current selection in the editor.
23388      */
23389     updateToolbar: function(){
23390
23391         if(!this.editorcore.activated){
23392             this.editor.onFirstFocus();
23393             return;
23394         }
23395
23396         var btns = this.tb.items.map, 
23397             doc = this.editorcore.doc,
23398             frameId = this.editorcore.frameId;
23399
23400         if(!this.disable.font && !Roo.isSafari){
23401             /*
23402             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23403             if(name != this.fontSelect.dom.value){
23404                 this.fontSelect.dom.value = name;
23405             }
23406             */
23407         }
23408         if(!this.disable.format){
23409             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23410             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23411             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23412             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23413         }
23414         if(!this.disable.alignments){
23415             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23416             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23417             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23418         }
23419         if(!Roo.isSafari && !this.disable.lists){
23420             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23421             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23422         }
23423         
23424         var ans = this.editorcore.getAllAncestors();
23425         if (this.formatCombo) {
23426             
23427             
23428             var store = this.formatCombo.store;
23429             this.formatCombo.setValue("");
23430             for (var i =0; i < ans.length;i++) {
23431                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23432                     // select it..
23433                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23434                     break;
23435                 }
23436             }
23437         }
23438         
23439         
23440         
23441         // hides menus... - so this cant be on a menu...
23442         Roo.menu.MenuMgr.hideAll();
23443
23444         //this.editorsyncValue();
23445     },
23446    
23447     
23448     createFontOptions : function(){
23449         var buf = [], fs = this.fontFamilies, ff, lc;
23450         
23451         
23452         
23453         for(var i = 0, len = fs.length; i< len; i++){
23454             ff = fs[i];
23455             lc = ff.toLowerCase();
23456             buf.push(
23457                 '<option value="',lc,'" style="font-family:',ff,';"',
23458                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23459                     ff,
23460                 '</option>'
23461             );
23462         }
23463         return buf.join('');
23464     },
23465     
23466     toggleSourceEdit : function(sourceEditMode){
23467         
23468         Roo.log("toolbar toogle");
23469         if(sourceEditMode === undefined){
23470             sourceEditMode = !this.sourceEditMode;
23471         }
23472         this.sourceEditMode = sourceEditMode === true;
23473         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23474         // just toggle the button?
23475         if(btn.pressed !== this.sourceEditMode){
23476             btn.toggle(this.sourceEditMode);
23477             return;
23478         }
23479         
23480         if(sourceEditMode){
23481             Roo.log("disabling buttons");
23482             this.tb.items.each(function(item){
23483                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23484                     item.disable();
23485                 }
23486             });
23487           
23488         }else{
23489             Roo.log("enabling buttons");
23490             if(this.editorcore.initialized){
23491                 this.tb.items.each(function(item){
23492                     item.enable();
23493                 });
23494             }
23495             
23496         }
23497         Roo.log("calling toggole on editor");
23498         // tell the editor that it's been pressed..
23499         this.editor.toggleSourceEdit(sourceEditMode);
23500        
23501     },
23502      /**
23503      * Object collection of toolbar tooltips for the buttons in the editor. The key
23504      * is the command id associated with that button and the value is a valid QuickTips object.
23505      * For example:
23506 <pre><code>
23507 {
23508     bold : {
23509         title: 'Bold (Ctrl+B)',
23510         text: 'Make the selected text bold.',
23511         cls: 'x-html-editor-tip'
23512     },
23513     italic : {
23514         title: 'Italic (Ctrl+I)',
23515         text: 'Make the selected text italic.',
23516         cls: 'x-html-editor-tip'
23517     },
23518     ...
23519 </code></pre>
23520     * @type Object
23521      */
23522     buttonTips : {
23523         bold : {
23524             title: 'Bold (Ctrl+B)',
23525             text: 'Make the selected text bold.',
23526             cls: 'x-html-editor-tip'
23527         },
23528         italic : {
23529             title: 'Italic (Ctrl+I)',
23530             text: 'Make the selected text italic.',
23531             cls: 'x-html-editor-tip'
23532         },
23533         underline : {
23534             title: 'Underline (Ctrl+U)',
23535             text: 'Underline the selected text.',
23536             cls: 'x-html-editor-tip'
23537         },
23538         strikethrough : {
23539             title: 'Strikethrough',
23540             text: 'Strikethrough the selected text.',
23541             cls: 'x-html-editor-tip'
23542         },
23543         increasefontsize : {
23544             title: 'Grow Text',
23545             text: 'Increase the font size.',
23546             cls: 'x-html-editor-tip'
23547         },
23548         decreasefontsize : {
23549             title: 'Shrink Text',
23550             text: 'Decrease the font size.',
23551             cls: 'x-html-editor-tip'
23552         },
23553         backcolor : {
23554             title: 'Text Highlight Color',
23555             text: 'Change the background color of the selected text.',
23556             cls: 'x-html-editor-tip'
23557         },
23558         forecolor : {
23559             title: 'Font Color',
23560             text: 'Change the color of the selected text.',
23561             cls: 'x-html-editor-tip'
23562         },
23563         justifyleft : {
23564             title: 'Align Text Left',
23565             text: 'Align text to the left.',
23566             cls: 'x-html-editor-tip'
23567         },
23568         justifycenter : {
23569             title: 'Center Text',
23570             text: 'Center text in the editor.',
23571             cls: 'x-html-editor-tip'
23572         },
23573         justifyright : {
23574             title: 'Align Text Right',
23575             text: 'Align text to the right.',
23576             cls: 'x-html-editor-tip'
23577         },
23578         insertunorderedlist : {
23579             title: 'Bullet List',
23580             text: 'Start a bulleted list.',
23581             cls: 'x-html-editor-tip'
23582         },
23583         insertorderedlist : {
23584             title: 'Numbered List',
23585             text: 'Start a numbered list.',
23586             cls: 'x-html-editor-tip'
23587         },
23588         createlink : {
23589             title: 'Hyperlink',
23590             text: 'Make the selected text a hyperlink.',
23591             cls: 'x-html-editor-tip'
23592         },
23593         sourceedit : {
23594             title: 'Source Edit',
23595             text: 'Switch to source editing mode.',
23596             cls: 'x-html-editor-tip'
23597         }
23598     },
23599     // private
23600     onDestroy : function(){
23601         if(this.rendered){
23602             
23603             this.tb.items.each(function(item){
23604                 if(item.menu){
23605                     item.menu.removeAll();
23606                     if(item.menu.el){
23607                         item.menu.el.destroy();
23608                     }
23609                 }
23610                 item.destroy();
23611             });
23612              
23613         }
23614     },
23615     onFirstFocus: function() {
23616         this.tb.items.each(function(item){
23617            item.enable();
23618         });
23619     }
23620 });
23621
23622
23623
23624
23625 // <script type="text/javascript">
23626 /*
23627  * Based on
23628  * Ext JS Library 1.1.1
23629  * Copyright(c) 2006-2007, Ext JS, LLC.
23630  *  
23631  
23632  */
23633
23634  
23635 /**
23636  * @class Roo.form.HtmlEditor.ToolbarContext
23637  * Context Toolbar
23638  * 
23639  * Usage:
23640  *
23641  new Roo.form.HtmlEditor({
23642     ....
23643     toolbars : [
23644         { xtype: 'ToolbarStandard', styles : {} }
23645         { xtype: 'ToolbarContext', disable : {} }
23646     ]
23647 })
23648
23649      
23650  * 
23651  * @config : {Object} disable List of elements to disable.. (not done yet.)
23652  * @config : {Object} styles  Map of styles available.
23653  * 
23654  */
23655
23656 Roo.form.HtmlEditor.ToolbarContext = function(config)
23657 {
23658     
23659     Roo.apply(this, config);
23660     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23661     // dont call parent... till later.
23662     this.styles = this.styles || {};
23663 }
23664
23665  
23666
23667 Roo.form.HtmlEditor.ToolbarContext.types = {
23668     'IMG' : {
23669         width : {
23670             title: "Width",
23671             width: 40
23672         },
23673         height:  {
23674             title: "Height",
23675             width: 40
23676         },
23677         align: {
23678             title: "Align",
23679             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23680             width : 80
23681             
23682         },
23683         border: {
23684             title: "Border",
23685             width: 40
23686         },
23687         alt: {
23688             title: "Alt",
23689             width: 120
23690         },
23691         src : {
23692             title: "Src",
23693             width: 220
23694         }
23695         
23696     },
23697     'A' : {
23698         name : {
23699             title: "Name",
23700             width: 50
23701         },
23702         target:  {
23703             title: "Target",
23704             width: 120
23705         },
23706         href:  {
23707             title: "Href",
23708             width: 220
23709         } // border?
23710         
23711     },
23712     'TABLE' : {
23713         rows : {
23714             title: "Rows",
23715             width: 20
23716         },
23717         cols : {
23718             title: "Cols",
23719             width: 20
23720         },
23721         width : {
23722             title: "Width",
23723             width: 40
23724         },
23725         height : {
23726             title: "Height",
23727             width: 40
23728         },
23729         border : {
23730             title: "Border",
23731             width: 20
23732         }
23733     },
23734     'TD' : {
23735         width : {
23736             title: "Width",
23737             width: 40
23738         },
23739         height : {
23740             title: "Height",
23741             width: 40
23742         },   
23743         align: {
23744             title: "Align",
23745             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23746             width: 80
23747         },
23748         valign: {
23749             title: "Valign",
23750             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23751             width: 80
23752         },
23753         colspan: {
23754             title: "Colspan",
23755             width: 20
23756             
23757         },
23758          'font-family'  : {
23759             title : "Font",
23760             style : 'fontFamily',
23761             displayField: 'display',
23762             optname : 'font-family',
23763             width: 140
23764         }
23765     },
23766     'INPUT' : {
23767         name : {
23768             title: "name",
23769             width: 120
23770         },
23771         value : {
23772             title: "Value",
23773             width: 120
23774         },
23775         width : {
23776             title: "Width",
23777             width: 40
23778         }
23779     },
23780     'LABEL' : {
23781         'for' : {
23782             title: "For",
23783             width: 120
23784         }
23785     },
23786     'TEXTAREA' : {
23787           name : {
23788             title: "name",
23789             width: 120
23790         },
23791         rows : {
23792             title: "Rows",
23793             width: 20
23794         },
23795         cols : {
23796             title: "Cols",
23797             width: 20
23798         }
23799     },
23800     'SELECT' : {
23801         name : {
23802             title: "name",
23803             width: 120
23804         },
23805         selectoptions : {
23806             title: "Options",
23807             width: 200
23808         }
23809     },
23810     
23811     // should we really allow this??
23812     // should this just be 
23813     'BODY' : {
23814         title : {
23815             title: "Title",
23816             width: 200,
23817             disabled : true
23818         }
23819     },
23820     'SPAN' : {
23821         'font-family'  : {
23822             title : "Font",
23823             style : 'fontFamily',
23824             displayField: 'display',
23825             optname : 'font-family',
23826             width: 140
23827         }
23828     },
23829     'DIV' : {
23830         'font-family'  : {
23831             title : "Font",
23832             style : 'fontFamily',
23833             displayField: 'display',
23834             optname : 'font-family',
23835             width: 140
23836         }
23837     },
23838      'P' : {
23839         'font-family'  : {
23840             title : "Font",
23841             style : 'fontFamily',
23842             displayField: 'display',
23843             optname : 'font-family',
23844             width: 140
23845         }
23846     },
23847     
23848     '*' : {
23849         // empty..
23850     }
23851
23852 };
23853
23854 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23855 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23856
23857 Roo.form.HtmlEditor.ToolbarContext.options = {
23858         'font-family'  : [ 
23859                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23860                 [ 'Courier New', 'Courier New'],
23861                 [ 'Tahoma', 'Tahoma'],
23862                 [ 'Times New Roman,serif', 'Times'],
23863                 [ 'Verdana','Verdana' ]
23864         ]
23865 };
23866
23867 // fixme - these need to be configurable..
23868  
23869
23870 //Roo.form.HtmlEditor.ToolbarContext.types
23871
23872
23873 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23874     
23875     tb: false,
23876     
23877     rendered: false,
23878     
23879     editor : false,
23880     editorcore : false,
23881     /**
23882      * @cfg {Object} disable  List of toolbar elements to disable
23883          
23884      */
23885     disable : false,
23886     /**
23887      * @cfg {Object} styles List of styles 
23888      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23889      *
23890      * These must be defined in the page, so they get rendered correctly..
23891      * .headline { }
23892      * TD.underline { }
23893      * 
23894      */
23895     styles : false,
23896     
23897     options: false,
23898     
23899     toolbars : false,
23900     
23901     init : function(editor)
23902     {
23903         this.editor = editor;
23904         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23905         var editorcore = this.editorcore;
23906         
23907         var fid = editorcore.frameId;
23908         var etb = this;
23909         function btn(id, toggle, handler){
23910             var xid = fid + '-'+ id ;
23911             return {
23912                 id : xid,
23913                 cmd : id,
23914                 cls : 'x-btn-icon x-edit-'+id,
23915                 enableToggle:toggle !== false,
23916                 scope: editorcore, // was editor...
23917                 handler:handler||editorcore.relayBtnCmd,
23918                 clickEvent:'mousedown',
23919                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23920                 tabIndex:-1
23921             };
23922         }
23923         // create a new element.
23924         var wdiv = editor.wrap.createChild({
23925                 tag: 'div'
23926             }, editor.wrap.dom.firstChild.nextSibling, true);
23927         
23928         // can we do this more than once??
23929         
23930          // stop form submits
23931       
23932  
23933         // disable everything...
23934         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23935         this.toolbars = {};
23936            
23937         for (var i in  ty) {
23938           
23939             this.toolbars[i] = this.buildToolbar(ty[i],i);
23940         }
23941         this.tb = this.toolbars.BODY;
23942         this.tb.el.show();
23943         this.buildFooter();
23944         this.footer.show();
23945         editor.on('hide', function( ) { this.footer.hide() }, this);
23946         editor.on('show', function( ) { this.footer.show() }, this);
23947         
23948          
23949         this.rendered = true;
23950         
23951         // the all the btns;
23952         editor.on('editorevent', this.updateToolbar, this);
23953         // other toolbars need to implement this..
23954         //editor.on('editmodechange', this.updateToolbar, this);
23955     },
23956     
23957     
23958     
23959     /**
23960      * Protected method that will not generally be called directly. It triggers
23961      * a toolbar update by reading the markup state of the current selection in the editor.
23962      *
23963      * Note you can force an update by calling on('editorevent', scope, false)
23964      */
23965     updateToolbar: function(editor,ev,sel){
23966
23967         //Roo.log(ev);
23968         // capture mouse up - this is handy for selecting images..
23969         // perhaps should go somewhere else...
23970         if(!this.editorcore.activated){
23971              this.editor.onFirstFocus();
23972             return;
23973         }
23974         
23975         
23976         
23977         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23978         // selectNode - might want to handle IE?
23979         if (ev &&
23980             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23981             ev.target && ev.target.tagName == 'IMG') {
23982             // they have click on an image...
23983             // let's see if we can change the selection...
23984             sel = ev.target;
23985          
23986               var nodeRange = sel.ownerDocument.createRange();
23987             try {
23988                 nodeRange.selectNode(sel);
23989             } catch (e) {
23990                 nodeRange.selectNodeContents(sel);
23991             }
23992             //nodeRange.collapse(true);
23993             var s = this.editorcore.win.getSelection();
23994             s.removeAllRanges();
23995             s.addRange(nodeRange);
23996         }  
23997         
23998       
23999         var updateFooter = sel ? false : true;
24000         
24001         
24002         var ans = this.editorcore.getAllAncestors();
24003         
24004         // pick
24005         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24006         
24007         if (!sel) { 
24008             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24009             sel = sel ? sel : this.editorcore.doc.body;
24010             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24011             
24012         }
24013         // pick a menu that exists..
24014         var tn = sel.tagName.toUpperCase();
24015         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24016         
24017         tn = sel.tagName.toUpperCase();
24018         
24019         var lastSel = this.tb.selectedNode;
24020         
24021         this.tb.selectedNode = sel;
24022         
24023         // if current menu does not match..
24024         
24025         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24026                 
24027             this.tb.el.hide();
24028             ///console.log("show: " + tn);
24029             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24030             this.tb.el.show();
24031             // update name
24032             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24033             
24034             
24035             // update attributes
24036             if (this.tb.fields) {
24037                 this.tb.fields.each(function(e) {
24038                     if (e.stylename) {
24039                         e.setValue(sel.style[e.stylename]);
24040                         return;
24041                     } 
24042                    e.setValue(sel.getAttribute(e.attrname));
24043                 });
24044             }
24045             
24046             var hasStyles = false;
24047             for(var i in this.styles) {
24048                 hasStyles = true;
24049                 break;
24050             }
24051             
24052             // update styles
24053             if (hasStyles) { 
24054                 var st = this.tb.fields.item(0);
24055                 
24056                 st.store.removeAll();
24057                
24058                 
24059                 var cn = sel.className.split(/\s+/);
24060                 
24061                 var avs = [];
24062                 if (this.styles['*']) {
24063                     
24064                     Roo.each(this.styles['*'], function(v) {
24065                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24066                     });
24067                 }
24068                 if (this.styles[tn]) { 
24069                     Roo.each(this.styles[tn], function(v) {
24070                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24071                     });
24072                 }
24073                 
24074                 st.store.loadData(avs);
24075                 st.collapse();
24076                 st.setValue(cn);
24077             }
24078             // flag our selected Node.
24079             this.tb.selectedNode = sel;
24080            
24081            
24082             Roo.menu.MenuMgr.hideAll();
24083
24084         }
24085         
24086         if (!updateFooter) {
24087             //this.footDisp.dom.innerHTML = ''; 
24088             return;
24089         }
24090         // update the footer
24091         //
24092         var html = '';
24093         
24094         this.footerEls = ans.reverse();
24095         Roo.each(this.footerEls, function(a,i) {
24096             if (!a) { return; }
24097             html += html.length ? ' &gt; '  :  '';
24098             
24099             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24100             
24101         });
24102        
24103         // 
24104         var sz = this.footDisp.up('td').getSize();
24105         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24106         this.footDisp.dom.style.marginLeft = '5px';
24107         
24108         this.footDisp.dom.style.overflow = 'hidden';
24109         
24110         this.footDisp.dom.innerHTML = html;
24111             
24112         //this.editorsyncValue();
24113     },
24114      
24115     
24116    
24117        
24118     // private
24119     onDestroy : function(){
24120         if(this.rendered){
24121             
24122             this.tb.items.each(function(item){
24123                 if(item.menu){
24124                     item.menu.removeAll();
24125                     if(item.menu.el){
24126                         item.menu.el.destroy();
24127                     }
24128                 }
24129                 item.destroy();
24130             });
24131              
24132         }
24133     },
24134     onFirstFocus: function() {
24135         // need to do this for all the toolbars..
24136         this.tb.items.each(function(item){
24137            item.enable();
24138         });
24139     },
24140     buildToolbar: function(tlist, nm)
24141     {
24142         var editor = this.editor;
24143         var editorcore = this.editorcore;
24144          // create a new element.
24145         var wdiv = editor.wrap.createChild({
24146                 tag: 'div'
24147             }, editor.wrap.dom.firstChild.nextSibling, true);
24148         
24149        
24150         var tb = new Roo.Toolbar(wdiv);
24151         // add the name..
24152         
24153         tb.add(nm+ ":&nbsp;");
24154         
24155         var styles = [];
24156         for(var i in this.styles) {
24157             styles.push(i);
24158         }
24159         
24160         // styles...
24161         if (styles && styles.length) {
24162             
24163             // this needs a multi-select checkbox...
24164             tb.addField( new Roo.form.ComboBox({
24165                 store: new Roo.data.SimpleStore({
24166                     id : 'val',
24167                     fields: ['val', 'selected'],
24168                     data : [] 
24169                 }),
24170                 name : '-roo-edit-className',
24171                 attrname : 'className',
24172                 displayField: 'val',
24173                 typeAhead: false,
24174                 mode: 'local',
24175                 editable : false,
24176                 triggerAction: 'all',
24177                 emptyText:'Select Style',
24178                 selectOnFocus:true,
24179                 width: 130,
24180                 listeners : {
24181                     'select': function(c, r, i) {
24182                         // initial support only for on class per el..
24183                         tb.selectedNode.className =  r ? r.get('val') : '';
24184                         editorcore.syncValue();
24185                     }
24186                 }
24187     
24188             }));
24189         }
24190         
24191         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24192         var tbops = tbc.options;
24193         
24194         for (var i in tlist) {
24195             
24196             var item = tlist[i];
24197             tb.add(item.title + ":&nbsp;");
24198             
24199             
24200             //optname == used so you can configure the options available..
24201             var opts = item.opts ? item.opts : false;
24202             if (item.optname) {
24203                 opts = tbops[item.optname];
24204            
24205             }
24206             
24207             if (opts) {
24208                 // opts == pulldown..
24209                 tb.addField( new Roo.form.ComboBox({
24210                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24211                         id : 'val',
24212                         fields: ['val', 'display'],
24213                         data : opts  
24214                     }),
24215                     name : '-roo-edit-' + i,
24216                     attrname : i,
24217                     stylename : item.style ? item.style : false,
24218                     displayField: item.displayField ? item.displayField : 'val',
24219                     valueField :  'val',
24220                     typeAhead: false,
24221                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24222                     editable : false,
24223                     triggerAction: 'all',
24224                     emptyText:'Select',
24225                     selectOnFocus:true,
24226                     width: item.width ? item.width  : 130,
24227                     listeners : {
24228                         'select': function(c, r, i) {
24229                             if (c.stylename) {
24230                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24231                                 return;
24232                             }
24233                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24234                         }
24235                     }
24236
24237                 }));
24238                 continue;
24239                     
24240                  
24241                 
24242                 tb.addField( new Roo.form.TextField({
24243                     name: i,
24244                     width: 100,
24245                     //allowBlank:false,
24246                     value: ''
24247                 }));
24248                 continue;
24249             }
24250             tb.addField( new Roo.form.TextField({
24251                 name: '-roo-edit-' + i,
24252                 attrname : i,
24253                 
24254                 width: item.width,
24255                 //allowBlank:true,
24256                 value: '',
24257                 listeners: {
24258                     'change' : function(f, nv, ov) {
24259                         tb.selectedNode.setAttribute(f.attrname, nv);
24260                         editorcore.syncValue();
24261                     }
24262                 }
24263             }));
24264              
24265         }
24266         
24267         var _this = this;
24268         
24269         if(nm == 'BODY'){
24270             tb.addSeparator();
24271         
24272             tb.addButton( {
24273                 text: 'Stylesheets',
24274
24275                 listeners : {
24276                     click : function ()
24277                     {
24278                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24279                     }
24280                 }
24281             });
24282         }
24283         
24284         tb.addFill();
24285         tb.addButton( {
24286             text: 'Remove Tag',
24287     
24288             listeners : {
24289                 click : function ()
24290                 {
24291                     // remove
24292                     // undo does not work.
24293                      
24294                     var sn = tb.selectedNode;
24295                     
24296                     var pn = sn.parentNode;
24297                     
24298                     var stn =  sn.childNodes[0];
24299                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24300                     while (sn.childNodes.length) {
24301                         var node = sn.childNodes[0];
24302                         sn.removeChild(node);
24303                         //Roo.log(node);
24304                         pn.insertBefore(node, sn);
24305                         
24306                     }
24307                     pn.removeChild(sn);
24308                     var range = editorcore.createRange();
24309         
24310                     range.setStart(stn,0);
24311                     range.setEnd(en,0); //????
24312                     //range.selectNode(sel);
24313                     
24314                     
24315                     var selection = editorcore.getSelection();
24316                     selection.removeAllRanges();
24317                     selection.addRange(range);
24318                     
24319                     
24320                     
24321                     //_this.updateToolbar(null, null, pn);
24322                     _this.updateToolbar(null, null, null);
24323                     _this.footDisp.dom.innerHTML = ''; 
24324                 }
24325             }
24326             
24327                     
24328                 
24329             
24330         });
24331         
24332         
24333         tb.el.on('click', function(e){
24334             e.preventDefault(); // what does this do?
24335         });
24336         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24337         tb.el.hide();
24338         tb.name = nm;
24339         // dont need to disable them... as they will get hidden
24340         return tb;
24341          
24342         
24343     },
24344     buildFooter : function()
24345     {
24346         
24347         var fel = this.editor.wrap.createChild();
24348         this.footer = new Roo.Toolbar(fel);
24349         // toolbar has scrolly on left / right?
24350         var footDisp= new Roo.Toolbar.Fill();
24351         var _t = this;
24352         this.footer.add(
24353             {
24354                 text : '&lt;',
24355                 xtype: 'Button',
24356                 handler : function() {
24357                     _t.footDisp.scrollTo('left',0,true)
24358                 }
24359             }
24360         );
24361         this.footer.add( footDisp );
24362         this.footer.add( 
24363             {
24364                 text : '&gt;',
24365                 xtype: 'Button',
24366                 handler : function() {
24367                     // no animation..
24368                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24369                 }
24370             }
24371         );
24372         var fel = Roo.get(footDisp.el);
24373         fel.addClass('x-editor-context');
24374         this.footDispWrap = fel; 
24375         this.footDispWrap.overflow  = 'hidden';
24376         
24377         this.footDisp = fel.createChild();
24378         this.footDispWrap.on('click', this.onContextClick, this)
24379         
24380         
24381     },
24382     onContextClick : function (ev,dom)
24383     {
24384         ev.preventDefault();
24385         var  cn = dom.className;
24386         //Roo.log(cn);
24387         if (!cn.match(/x-ed-loc-/)) {
24388             return;
24389         }
24390         var n = cn.split('-').pop();
24391         var ans = this.footerEls;
24392         var sel = ans[n];
24393         
24394          // pick
24395         var range = this.editorcore.createRange();
24396         
24397         range.selectNodeContents(sel);
24398         //range.selectNode(sel);
24399         
24400         
24401         var selection = this.editorcore.getSelection();
24402         selection.removeAllRanges();
24403         selection.addRange(range);
24404         
24405         
24406         
24407         this.updateToolbar(null, null, sel);
24408         
24409         
24410     }
24411     
24412     
24413     
24414     
24415     
24416 });
24417
24418
24419
24420
24421
24422 /*
24423  * Based on:
24424  * Ext JS Library 1.1.1
24425  * Copyright(c) 2006-2007, Ext JS, LLC.
24426  *
24427  * Originally Released Under LGPL - original licence link has changed is not relivant.
24428  *
24429  * Fork - LGPL
24430  * <script type="text/javascript">
24431  */
24432  
24433 /**
24434  * @class Roo.form.BasicForm
24435  * @extends Roo.util.Observable
24436  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24437  * @constructor
24438  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24439  * @param {Object} config Configuration options
24440  */
24441 Roo.form.BasicForm = function(el, config){
24442     this.allItems = [];
24443     this.childForms = [];
24444     Roo.apply(this, config);
24445     /*
24446      * The Roo.form.Field items in this form.
24447      * @type MixedCollection
24448      */
24449      
24450      
24451     this.items = new Roo.util.MixedCollection(false, function(o){
24452         return o.id || (o.id = Roo.id());
24453     });
24454     this.addEvents({
24455         /**
24456          * @event beforeaction
24457          * Fires before any action is performed. Return false to cancel the action.
24458          * @param {Form} this
24459          * @param {Action} action The action to be performed
24460          */
24461         beforeaction: true,
24462         /**
24463          * @event actionfailed
24464          * Fires when an action fails.
24465          * @param {Form} this
24466          * @param {Action} action The action that failed
24467          */
24468         actionfailed : true,
24469         /**
24470          * @event actioncomplete
24471          * Fires when an action is completed.
24472          * @param {Form} this
24473          * @param {Action} action The action that completed
24474          */
24475         actioncomplete : true
24476     });
24477     if(el){
24478         this.initEl(el);
24479     }
24480     Roo.form.BasicForm.superclass.constructor.call(this);
24481     
24482     Roo.form.BasicForm.popover.apply();
24483 };
24484
24485 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24486     /**
24487      * @cfg {String} method
24488      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24489      */
24490     /**
24491      * @cfg {DataReader} reader
24492      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24493      * This is optional as there is built-in support for processing JSON.
24494      */
24495     /**
24496      * @cfg {DataReader} errorReader
24497      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24498      * This is completely optional as there is built-in support for processing JSON.
24499      */
24500     /**
24501      * @cfg {String} url
24502      * The URL to use for form actions if one isn't supplied in the action options.
24503      */
24504     /**
24505      * @cfg {Boolean} fileUpload
24506      * Set to true if this form is a file upload.
24507      */
24508      
24509     /**
24510      * @cfg {Object} baseParams
24511      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24512      */
24513      /**
24514      
24515     /**
24516      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24517      */
24518     timeout: 30,
24519
24520     // private
24521     activeAction : null,
24522
24523     /**
24524      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24525      * or setValues() data instead of when the form was first created.
24526      */
24527     trackResetOnLoad : false,
24528     
24529     
24530     /**
24531      * childForms - used for multi-tab forms
24532      * @type {Array}
24533      */
24534     childForms : false,
24535     
24536     /**
24537      * allItems - full list of fields.
24538      * @type {Array}
24539      */
24540     allItems : false,
24541     
24542     /**
24543      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24544      * element by passing it or its id or mask the form itself by passing in true.
24545      * @type Mixed
24546      */
24547     waitMsgTarget : false,
24548     
24549     /**
24550      * @type Boolean
24551      */
24552     disableMask : false,
24553     
24554     /**
24555      * @cfg {Boolean} errorMask (true|false) default false
24556      */
24557     errorMask : false,
24558     
24559     /**
24560      * @cfg {Number} maskOffset Default 100
24561      */
24562     maskOffset : 100,
24563
24564     // private
24565     initEl : function(el){
24566         this.el = Roo.get(el);
24567         this.id = this.el.id || Roo.id();
24568         this.el.on('submit', this.onSubmit, this);
24569         this.el.addClass('x-form');
24570     },
24571
24572     // private
24573     onSubmit : function(e){
24574         e.stopEvent();
24575     },
24576
24577     /**
24578      * Returns true if client-side validation on the form is successful.
24579      * @return Boolean
24580      */
24581     isValid : function(){
24582         var valid = true;
24583         var target = false;
24584         this.items.each(function(f){
24585             if(f.validate()){
24586                 return;
24587             }
24588             
24589             valid = false;
24590                 
24591             if(!target && f.el.isVisible(true)){
24592                 target = f;
24593             }
24594         });
24595         
24596         if(this.errorMask && !valid){
24597             Roo.form.BasicForm.popover.mask(this, target);
24598         }
24599         
24600         return valid;
24601     },
24602
24603     /**
24604      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24605      * @return Boolean
24606      */
24607     isDirty : function(){
24608         var dirty = false;
24609         this.items.each(function(f){
24610            if(f.isDirty()){
24611                dirty = true;
24612                return false;
24613            }
24614         });
24615         return dirty;
24616     },
24617     
24618     /**
24619      * Returns true if any fields in this form have changed since their original load. (New version)
24620      * @return Boolean
24621      */
24622     
24623     hasChanged : function()
24624     {
24625         var dirty = false;
24626         this.items.each(function(f){
24627            if(f.hasChanged()){
24628                dirty = true;
24629                return false;
24630            }
24631         });
24632         return dirty;
24633         
24634     },
24635     /**
24636      * Resets all hasChanged to 'false' -
24637      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24638      * So hasChanged storage is only to be used for this purpose
24639      * @return Boolean
24640      */
24641     resetHasChanged : function()
24642     {
24643         this.items.each(function(f){
24644            f.resetHasChanged();
24645         });
24646         
24647     },
24648     
24649     
24650     /**
24651      * Performs a predefined action (submit or load) or custom actions you define on this form.
24652      * @param {String} actionName The name of the action type
24653      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24654      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24655      * accept other config options):
24656      * <pre>
24657 Property          Type             Description
24658 ----------------  ---------------  ----------------------------------------------------------------------------------
24659 url               String           The url for the action (defaults to the form's url)
24660 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24661 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24662 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24663                                    validate the form on the client (defaults to false)
24664      * </pre>
24665      * @return {BasicForm} this
24666      */
24667     doAction : function(action, options){
24668         if(typeof action == 'string'){
24669             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24670         }
24671         if(this.fireEvent('beforeaction', this, action) !== false){
24672             this.beforeAction(action);
24673             action.run.defer(100, action);
24674         }
24675         return this;
24676     },
24677
24678     /**
24679      * Shortcut to do a submit action.
24680      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24681      * @return {BasicForm} this
24682      */
24683     submit : function(options){
24684         this.doAction('submit', options);
24685         return this;
24686     },
24687
24688     /**
24689      * Shortcut to do a load action.
24690      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24691      * @return {BasicForm} this
24692      */
24693     load : function(options){
24694         this.doAction('load', options);
24695         return this;
24696     },
24697
24698     /**
24699      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24700      * @param {Record} record The record to edit
24701      * @return {BasicForm} this
24702      */
24703     updateRecord : function(record){
24704         record.beginEdit();
24705         var fs = record.fields;
24706         fs.each(function(f){
24707             var field = this.findField(f.name);
24708             if(field){
24709                 record.set(f.name, field.getValue());
24710             }
24711         }, this);
24712         record.endEdit();
24713         return this;
24714     },
24715
24716     /**
24717      * Loads an Roo.data.Record into this form.
24718      * @param {Record} record The record to load
24719      * @return {BasicForm} this
24720      */
24721     loadRecord : function(record){
24722         this.setValues(record.data);
24723         return this;
24724     },
24725
24726     // private
24727     beforeAction : function(action){
24728         var o = action.options;
24729         
24730         if(!this.disableMask) {
24731             if(this.waitMsgTarget === true){
24732                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24733             }else if(this.waitMsgTarget){
24734                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24735                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24736             }else {
24737                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24738             }
24739         }
24740         
24741          
24742     },
24743
24744     // private
24745     afterAction : function(action, success){
24746         this.activeAction = null;
24747         var o = action.options;
24748         
24749         if(!this.disableMask) {
24750             if(this.waitMsgTarget === true){
24751                 this.el.unmask();
24752             }else if(this.waitMsgTarget){
24753                 this.waitMsgTarget.unmask();
24754             }else{
24755                 Roo.MessageBox.updateProgress(1);
24756                 Roo.MessageBox.hide();
24757             }
24758         }
24759         
24760         if(success){
24761             if(o.reset){
24762                 this.reset();
24763             }
24764             Roo.callback(o.success, o.scope, [this, action]);
24765             this.fireEvent('actioncomplete', this, action);
24766             
24767         }else{
24768             
24769             // failure condition..
24770             // we have a scenario where updates need confirming.
24771             // eg. if a locking scenario exists..
24772             // we look for { errors : { needs_confirm : true }} in the response.
24773             if (
24774                 (typeof(action.result) != 'undefined')  &&
24775                 (typeof(action.result.errors) != 'undefined')  &&
24776                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24777            ){
24778                 var _t = this;
24779                 Roo.MessageBox.confirm(
24780                     "Change requires confirmation",
24781                     action.result.errorMsg,
24782                     function(r) {
24783                         if (r != 'yes') {
24784                             return;
24785                         }
24786                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24787                     }
24788                     
24789                 );
24790                 
24791                 
24792                 
24793                 return;
24794             }
24795             
24796             Roo.callback(o.failure, o.scope, [this, action]);
24797             // show an error message if no failed handler is set..
24798             if (!this.hasListener('actionfailed')) {
24799                 Roo.MessageBox.alert("Error",
24800                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24801                         action.result.errorMsg :
24802                         "Saving Failed, please check your entries or try again"
24803                 );
24804             }
24805             
24806             this.fireEvent('actionfailed', this, action);
24807         }
24808         
24809     },
24810
24811     /**
24812      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24813      * @param {String} id The value to search for
24814      * @return Field
24815      */
24816     findField : function(id){
24817         var field = this.items.get(id);
24818         if(!field){
24819             this.items.each(function(f){
24820                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24821                     field = f;
24822                     return false;
24823                 }
24824             });
24825         }
24826         return field || null;
24827     },
24828
24829     /**
24830      * Add a secondary form to this one, 
24831      * Used to provide tabbed forms. One form is primary, with hidden values 
24832      * which mirror the elements from the other forms.
24833      * 
24834      * @param {Roo.form.Form} form to add.
24835      * 
24836      */
24837     addForm : function(form)
24838     {
24839        
24840         if (this.childForms.indexOf(form) > -1) {
24841             // already added..
24842             return;
24843         }
24844         this.childForms.push(form);
24845         var n = '';
24846         Roo.each(form.allItems, function (fe) {
24847             
24848             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24849             if (this.findField(n)) { // already added..
24850                 return;
24851             }
24852             var add = new Roo.form.Hidden({
24853                 name : n
24854             });
24855             add.render(this.el);
24856             
24857             this.add( add );
24858         }, this);
24859         
24860     },
24861     /**
24862      * Mark fields in this form invalid in bulk.
24863      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24864      * @return {BasicForm} this
24865      */
24866     markInvalid : function(errors){
24867         if(errors instanceof Array){
24868             for(var i = 0, len = errors.length; i < len; i++){
24869                 var fieldError = errors[i];
24870                 var f = this.findField(fieldError.id);
24871                 if(f){
24872                     f.markInvalid(fieldError.msg);
24873                 }
24874             }
24875         }else{
24876             var field, id;
24877             for(id in errors){
24878                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24879                     field.markInvalid(errors[id]);
24880                 }
24881             }
24882         }
24883         Roo.each(this.childForms || [], function (f) {
24884             f.markInvalid(errors);
24885         });
24886         
24887         return this;
24888     },
24889
24890     /**
24891      * Set values for fields in this form in bulk.
24892      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24893      * @return {BasicForm} this
24894      */
24895     setValues : function(values){
24896         if(values instanceof Array){ // array of objects
24897             for(var i = 0, len = values.length; i < len; i++){
24898                 var v = values[i];
24899                 var f = this.findField(v.id);
24900                 if(f){
24901                     f.setValue(v.value);
24902                     if(this.trackResetOnLoad){
24903                         f.originalValue = f.getValue();
24904                     }
24905                 }
24906             }
24907         }else{ // object hash
24908             var field, id;
24909             for(id in values){
24910                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24911                     
24912                     if (field.setFromData && 
24913                         field.valueField && 
24914                         field.displayField &&
24915                         // combos' with local stores can 
24916                         // be queried via setValue()
24917                         // to set their value..
24918                         (field.store && !field.store.isLocal)
24919                         ) {
24920                         // it's a combo
24921                         var sd = { };
24922                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24923                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24924                         field.setFromData(sd);
24925                         
24926                     } else {
24927                         field.setValue(values[id]);
24928                     }
24929                     
24930                     
24931                     if(this.trackResetOnLoad){
24932                         field.originalValue = field.getValue();
24933                     }
24934                 }
24935             }
24936         }
24937         this.resetHasChanged();
24938         
24939         
24940         Roo.each(this.childForms || [], function (f) {
24941             f.setValues(values);
24942             f.resetHasChanged();
24943         });
24944                 
24945         return this;
24946     },
24947  
24948     /**
24949      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24950      * they are returned as an array.
24951      * @param {Boolean} asString
24952      * @return {Object}
24953      */
24954     getValues : function(asString){
24955         if (this.childForms) {
24956             // copy values from the child forms
24957             Roo.each(this.childForms, function (f) {
24958                 this.setValues(f.getValues());
24959             }, this);
24960         }
24961         
24962         // use formdata
24963         if (typeof(FormData) != 'undefined' && asString !== true) {
24964             // this relies on a 'recent' version of chrome apparently...
24965             try {
24966                 var fd = (new FormData(this.el.dom)).entries();
24967                 var ret = {};
24968                 var ent = fd.next();
24969                 while (!ent.done) {
24970                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
24971                     ent = fd.next();
24972                 };
24973                 return ret;
24974             } catch(e) {
24975                 
24976             }
24977             
24978         }
24979         
24980         
24981         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24982         if(asString === true){
24983             return fs;
24984         }
24985         return Roo.urlDecode(fs);
24986     },
24987     
24988     /**
24989      * Returns the fields in this form as an object with key/value pairs. 
24990      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24991      * @return {Object}
24992      */
24993     getFieldValues : function(with_hidden)
24994     {
24995         if (this.childForms) {
24996             // copy values from the child forms
24997             // should this call getFieldValues - probably not as we do not currently copy
24998             // hidden fields when we generate..
24999             Roo.each(this.childForms, function (f) {
25000                 this.setValues(f.getValues());
25001             }, this);
25002         }
25003         
25004         var ret = {};
25005         this.items.each(function(f){
25006             if (!f.getName()) {
25007                 return;
25008             }
25009             var v = f.getValue();
25010             if (f.inputType =='radio') {
25011                 if (typeof(ret[f.getName()]) == 'undefined') {
25012                     ret[f.getName()] = ''; // empty..
25013                 }
25014                 
25015                 if (!f.el.dom.checked) {
25016                     return;
25017                     
25018                 }
25019                 v = f.el.dom.value;
25020                 
25021             }
25022             
25023             // not sure if this supported any more..
25024             if ((typeof(v) == 'object') && f.getRawValue) {
25025                 v = f.getRawValue() ; // dates..
25026             }
25027             // combo boxes where name != hiddenName...
25028             if (f.name != f.getName()) {
25029                 ret[f.name] = f.getRawValue();
25030             }
25031             ret[f.getName()] = v;
25032         });
25033         
25034         return ret;
25035     },
25036
25037     /**
25038      * Clears all invalid messages in this form.
25039      * @return {BasicForm} this
25040      */
25041     clearInvalid : function(){
25042         this.items.each(function(f){
25043            f.clearInvalid();
25044         });
25045         
25046         Roo.each(this.childForms || [], function (f) {
25047             f.clearInvalid();
25048         });
25049         
25050         
25051         return this;
25052     },
25053
25054     /**
25055      * Resets this form.
25056      * @return {BasicForm} this
25057      */
25058     reset : function(){
25059         this.items.each(function(f){
25060             f.reset();
25061         });
25062         
25063         Roo.each(this.childForms || [], function (f) {
25064             f.reset();
25065         });
25066         this.resetHasChanged();
25067         
25068         return this;
25069     },
25070
25071     /**
25072      * Add Roo.form components to this form.
25073      * @param {Field} field1
25074      * @param {Field} field2 (optional)
25075      * @param {Field} etc (optional)
25076      * @return {BasicForm} this
25077      */
25078     add : function(){
25079         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25080         return this;
25081     },
25082
25083
25084     /**
25085      * Removes a field from the items collection (does NOT remove its markup).
25086      * @param {Field} field
25087      * @return {BasicForm} this
25088      */
25089     remove : function(field){
25090         this.items.remove(field);
25091         return this;
25092     },
25093
25094     /**
25095      * Looks at the fields in this form, checks them for an id attribute,
25096      * and calls applyTo on the existing dom element with that id.
25097      * @return {BasicForm} this
25098      */
25099     render : function(){
25100         this.items.each(function(f){
25101             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25102                 f.applyTo(f.id);
25103             }
25104         });
25105         return this;
25106     },
25107
25108     /**
25109      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25110      * @param {Object} values
25111      * @return {BasicForm} this
25112      */
25113     applyToFields : function(o){
25114         this.items.each(function(f){
25115            Roo.apply(f, o);
25116         });
25117         return this;
25118     },
25119
25120     /**
25121      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25122      * @param {Object} values
25123      * @return {BasicForm} this
25124      */
25125     applyIfToFields : function(o){
25126         this.items.each(function(f){
25127            Roo.applyIf(f, o);
25128         });
25129         return this;
25130     }
25131 });
25132
25133 // back compat
25134 Roo.BasicForm = Roo.form.BasicForm;
25135
25136 Roo.apply(Roo.form.BasicForm, {
25137     
25138     popover : {
25139         
25140         padding : 5,
25141         
25142         isApplied : false,
25143         
25144         isMasked : false,
25145         
25146         form : false,
25147         
25148         target : false,
25149         
25150         intervalID : false,
25151         
25152         maskEl : false,
25153         
25154         apply : function()
25155         {
25156             if(this.isApplied){
25157                 return;
25158             }
25159             
25160             this.maskEl = {
25161                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25162                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25163                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25164                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25165             };
25166             
25167             this.maskEl.top.enableDisplayMode("block");
25168             this.maskEl.left.enableDisplayMode("block");
25169             this.maskEl.bottom.enableDisplayMode("block");
25170             this.maskEl.right.enableDisplayMode("block");
25171             
25172             Roo.get(document.body).on('click', function(){
25173                 this.unmask();
25174             }, this);
25175             
25176             Roo.get(document.body).on('touchstart', function(){
25177                 this.unmask();
25178             }, this);
25179             
25180             this.isApplied = true
25181         },
25182         
25183         mask : function(form, target)
25184         {
25185             this.form = form;
25186             
25187             this.target = target;
25188             
25189             if(!this.form.errorMask || !target.el){
25190                 return;
25191             }
25192             
25193             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25194             
25195             var ot = this.target.el.calcOffsetsTo(scrollable);
25196             
25197             var scrollTo = ot[1] - this.form.maskOffset;
25198             
25199             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25200             
25201             scrollable.scrollTo('top', scrollTo);
25202             
25203             var el = this.target.wrap || this.target.el;
25204             
25205             var box = el.getBox();
25206             
25207             this.maskEl.top.setStyle('position', 'absolute');
25208             this.maskEl.top.setStyle('z-index', 10000);
25209             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25210             this.maskEl.top.setLeft(0);
25211             this.maskEl.top.setTop(0);
25212             this.maskEl.top.show();
25213             
25214             this.maskEl.left.setStyle('position', 'absolute');
25215             this.maskEl.left.setStyle('z-index', 10000);
25216             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25217             this.maskEl.left.setLeft(0);
25218             this.maskEl.left.setTop(box.y - this.padding);
25219             this.maskEl.left.show();
25220
25221             this.maskEl.bottom.setStyle('position', 'absolute');
25222             this.maskEl.bottom.setStyle('z-index', 10000);
25223             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25224             this.maskEl.bottom.setLeft(0);
25225             this.maskEl.bottom.setTop(box.bottom + this.padding);
25226             this.maskEl.bottom.show();
25227
25228             this.maskEl.right.setStyle('position', 'absolute');
25229             this.maskEl.right.setStyle('z-index', 10000);
25230             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25231             this.maskEl.right.setLeft(box.right + this.padding);
25232             this.maskEl.right.setTop(box.y - this.padding);
25233             this.maskEl.right.show();
25234
25235             this.intervalID = window.setInterval(function() {
25236                 Roo.form.BasicForm.popover.unmask();
25237             }, 10000);
25238
25239             window.onwheel = function(){ return false;};
25240             
25241             (function(){ this.isMasked = true; }).defer(500, this);
25242             
25243         },
25244         
25245         unmask : function()
25246         {
25247             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25248                 return;
25249             }
25250             
25251             this.maskEl.top.setStyle('position', 'absolute');
25252             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25253             this.maskEl.top.hide();
25254
25255             this.maskEl.left.setStyle('position', 'absolute');
25256             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25257             this.maskEl.left.hide();
25258
25259             this.maskEl.bottom.setStyle('position', 'absolute');
25260             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25261             this.maskEl.bottom.hide();
25262
25263             this.maskEl.right.setStyle('position', 'absolute');
25264             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25265             this.maskEl.right.hide();
25266             
25267             window.onwheel = function(){ return true;};
25268             
25269             if(this.intervalID){
25270                 window.clearInterval(this.intervalID);
25271                 this.intervalID = false;
25272             }
25273             
25274             this.isMasked = false;
25275             
25276         }
25277         
25278     }
25279     
25280 });/*
25281  * Based on:
25282  * Ext JS Library 1.1.1
25283  * Copyright(c) 2006-2007, Ext JS, LLC.
25284  *
25285  * Originally Released Under LGPL - original licence link has changed is not relivant.
25286  *
25287  * Fork - LGPL
25288  * <script type="text/javascript">
25289  */
25290
25291 /**
25292  * @class Roo.form.Form
25293  * @extends Roo.form.BasicForm
25294  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25295  * @constructor
25296  * @param {Object} config Configuration options
25297  */
25298 Roo.form.Form = function(config){
25299     var xitems =  [];
25300     if (config.items) {
25301         xitems = config.items;
25302         delete config.items;
25303     }
25304    
25305     
25306     Roo.form.Form.superclass.constructor.call(this, null, config);
25307     this.url = this.url || this.action;
25308     if(!this.root){
25309         this.root = new Roo.form.Layout(Roo.applyIf({
25310             id: Roo.id()
25311         }, config));
25312     }
25313     this.active = this.root;
25314     /**
25315      * Array of all the buttons that have been added to this form via {@link addButton}
25316      * @type Array
25317      */
25318     this.buttons = [];
25319     this.allItems = [];
25320     this.addEvents({
25321         /**
25322          * @event clientvalidation
25323          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25324          * @param {Form} this
25325          * @param {Boolean} valid true if the form has passed client-side validation
25326          */
25327         clientvalidation: true,
25328         /**
25329          * @event rendered
25330          * Fires when the form is rendered
25331          * @param {Roo.form.Form} form
25332          */
25333         rendered : true
25334     });
25335     
25336     if (this.progressUrl) {
25337             // push a hidden field onto the list of fields..
25338             this.addxtype( {
25339                     xns: Roo.form, 
25340                     xtype : 'Hidden', 
25341                     name : 'UPLOAD_IDENTIFIER' 
25342             });
25343         }
25344         
25345     
25346     Roo.each(xitems, this.addxtype, this);
25347     
25348 };
25349
25350 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25351     /**
25352      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25353      */
25354     /**
25355      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25356      */
25357     /**
25358      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25359      */
25360     buttonAlign:'center',
25361
25362     /**
25363      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25364      */
25365     minButtonWidth:75,
25366
25367     /**
25368      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25369      * This property cascades to child containers if not set.
25370      */
25371     labelAlign:'left',
25372
25373     /**
25374      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25375      * fires a looping event with that state. This is required to bind buttons to the valid
25376      * state using the config value formBind:true on the button.
25377      */
25378     monitorValid : false,
25379
25380     /**
25381      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25382      */
25383     monitorPoll : 200,
25384     
25385     /**
25386      * @cfg {String} progressUrl - Url to return progress data 
25387      */
25388     
25389     progressUrl : false,
25390     /**
25391      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25392      * sending a formdata with extra parameters - eg uploaded elements.
25393      */
25394     
25395     formData : false,
25396     
25397     /**
25398      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25399      * fields are added and the column is closed. If no fields are passed the column remains open
25400      * until end() is called.
25401      * @param {Object} config The config to pass to the column
25402      * @param {Field} field1 (optional)
25403      * @param {Field} field2 (optional)
25404      * @param {Field} etc (optional)
25405      * @return Column The column container object
25406      */
25407     column : function(c){
25408         var col = new Roo.form.Column(c);
25409         this.start(col);
25410         if(arguments.length > 1){ // duplicate code required because of Opera
25411             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25412             this.end();
25413         }
25414         return col;
25415     },
25416
25417     /**
25418      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25419      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25420      * until end() is called.
25421      * @param {Object} config The config to pass to the fieldset
25422      * @param {Field} field1 (optional)
25423      * @param {Field} field2 (optional)
25424      * @param {Field} etc (optional)
25425      * @return FieldSet The fieldset container object
25426      */
25427     fieldset : function(c){
25428         var fs = new Roo.form.FieldSet(c);
25429         this.start(fs);
25430         if(arguments.length > 1){ // duplicate code required because of Opera
25431             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25432             this.end();
25433         }
25434         return fs;
25435     },
25436
25437     /**
25438      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25439      * fields are added and the container is closed. If no fields are passed the container remains open
25440      * until end() is called.
25441      * @param {Object} config The config to pass to the Layout
25442      * @param {Field} field1 (optional)
25443      * @param {Field} field2 (optional)
25444      * @param {Field} etc (optional)
25445      * @return Layout The container object
25446      */
25447     container : function(c){
25448         var l = new Roo.form.Layout(c);
25449         this.start(l);
25450         if(arguments.length > 1){ // duplicate code required because of Opera
25451             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25452             this.end();
25453         }
25454         return l;
25455     },
25456
25457     /**
25458      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25459      * @param {Object} container A Roo.form.Layout or subclass of Layout
25460      * @return {Form} this
25461      */
25462     start : function(c){
25463         // cascade label info
25464         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25465         this.active.stack.push(c);
25466         c.ownerCt = this.active;
25467         this.active = c;
25468         return this;
25469     },
25470
25471     /**
25472      * Closes the current open container
25473      * @return {Form} this
25474      */
25475     end : function(){
25476         if(this.active == this.root){
25477             return this;
25478         }
25479         this.active = this.active.ownerCt;
25480         return this;
25481     },
25482
25483     /**
25484      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25485      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25486      * as the label of the field.
25487      * @param {Field} field1
25488      * @param {Field} field2 (optional)
25489      * @param {Field} etc. (optional)
25490      * @return {Form} this
25491      */
25492     add : function(){
25493         this.active.stack.push.apply(this.active.stack, arguments);
25494         this.allItems.push.apply(this.allItems,arguments);
25495         var r = [];
25496         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25497             if(a[i].isFormField){
25498                 r.push(a[i]);
25499             }
25500         }
25501         if(r.length > 0){
25502             Roo.form.Form.superclass.add.apply(this, r);
25503         }
25504         return this;
25505     },
25506     
25507
25508     
25509     
25510     
25511      /**
25512      * Find any element that has been added to a form, using it's ID or name
25513      * This can include framesets, columns etc. along with regular fields..
25514      * @param {String} id - id or name to find.
25515      
25516      * @return {Element} e - or false if nothing found.
25517      */
25518     findbyId : function(id)
25519     {
25520         var ret = false;
25521         if (!id) {
25522             return ret;
25523         }
25524         Roo.each(this.allItems, function(f){
25525             if (f.id == id || f.name == id ){
25526                 ret = f;
25527                 return false;
25528             }
25529         });
25530         return ret;
25531     },
25532
25533     
25534     
25535     /**
25536      * Render this form into the passed container. This should only be called once!
25537      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25538      * @return {Form} this
25539      */
25540     render : function(ct)
25541     {
25542         
25543         
25544         
25545         ct = Roo.get(ct);
25546         var o = this.autoCreate || {
25547             tag: 'form',
25548             method : this.method || 'POST',
25549             id : this.id || Roo.id()
25550         };
25551         this.initEl(ct.createChild(o));
25552
25553         this.root.render(this.el);
25554         
25555        
25556              
25557         this.items.each(function(f){
25558             f.render('x-form-el-'+f.id);
25559         });
25560
25561         if(this.buttons.length > 0){
25562             // tables are required to maintain order and for correct IE layout
25563             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25564                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25565                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25566             }}, null, true);
25567             var tr = tb.getElementsByTagName('tr')[0];
25568             for(var i = 0, len = this.buttons.length; i < len; i++) {
25569                 var b = this.buttons[i];
25570                 var td = document.createElement('td');
25571                 td.className = 'x-form-btn-td';
25572                 b.render(tr.appendChild(td));
25573             }
25574         }
25575         if(this.monitorValid){ // initialize after render
25576             this.startMonitoring();
25577         }
25578         this.fireEvent('rendered', this);
25579         return this;
25580     },
25581
25582     /**
25583      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25584      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25585      * object or a valid Roo.DomHelper element config
25586      * @param {Function} handler The function called when the button is clicked
25587      * @param {Object} scope (optional) The scope of the handler function
25588      * @return {Roo.Button}
25589      */
25590     addButton : function(config, handler, scope){
25591         var bc = {
25592             handler: handler,
25593             scope: scope,
25594             minWidth: this.minButtonWidth,
25595             hideParent:true
25596         };
25597         if(typeof config == "string"){
25598             bc.text = config;
25599         }else{
25600             Roo.apply(bc, config);
25601         }
25602         var btn = new Roo.Button(null, bc);
25603         this.buttons.push(btn);
25604         return btn;
25605     },
25606
25607      /**
25608      * Adds a series of form elements (using the xtype property as the factory method.
25609      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25610      * @param {Object} config 
25611      */
25612     
25613     addxtype : function()
25614     {
25615         var ar = Array.prototype.slice.call(arguments, 0);
25616         var ret = false;
25617         for(var i = 0; i < ar.length; i++) {
25618             if (!ar[i]) {
25619                 continue; // skip -- if this happends something invalid got sent, we 
25620                 // should ignore it, as basically that interface element will not show up
25621                 // and that should be pretty obvious!!
25622             }
25623             
25624             if (Roo.form[ar[i].xtype]) {
25625                 ar[i].form = this;
25626                 var fe = Roo.factory(ar[i], Roo.form);
25627                 if (!ret) {
25628                     ret = fe;
25629                 }
25630                 fe.form = this;
25631                 if (fe.store) {
25632                     fe.store.form = this;
25633                 }
25634                 if (fe.isLayout) {  
25635                          
25636                     this.start(fe);
25637                     this.allItems.push(fe);
25638                     if (fe.items && fe.addxtype) {
25639                         fe.addxtype.apply(fe, fe.items);
25640                         delete fe.items;
25641                     }
25642                      this.end();
25643                     continue;
25644                 }
25645                 
25646                 
25647                  
25648                 this.add(fe);
25649               //  console.log('adding ' + ar[i].xtype);
25650             }
25651             if (ar[i].xtype == 'Button') {  
25652                 //console.log('adding button');
25653                 //console.log(ar[i]);
25654                 this.addButton(ar[i]);
25655                 this.allItems.push(fe);
25656                 continue;
25657             }
25658             
25659             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25660                 alert('end is not supported on xtype any more, use items');
25661             //    this.end();
25662             //    //console.log('adding end');
25663             }
25664             
25665         }
25666         return ret;
25667     },
25668     
25669     /**
25670      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25671      * option "monitorValid"
25672      */
25673     startMonitoring : function(){
25674         if(!this.bound){
25675             this.bound = true;
25676             Roo.TaskMgr.start({
25677                 run : this.bindHandler,
25678                 interval : this.monitorPoll || 200,
25679                 scope: this
25680             });
25681         }
25682     },
25683
25684     /**
25685      * Stops monitoring of the valid state of this form
25686      */
25687     stopMonitoring : function(){
25688         this.bound = false;
25689     },
25690
25691     // private
25692     bindHandler : function(){
25693         if(!this.bound){
25694             return false; // stops binding
25695         }
25696         var valid = true;
25697         this.items.each(function(f){
25698             if(!f.isValid(true)){
25699                 valid = false;
25700                 return false;
25701             }
25702         });
25703         for(var i = 0, len = this.buttons.length; i < len; i++){
25704             var btn = this.buttons[i];
25705             if(btn.formBind === true && btn.disabled === valid){
25706                 btn.setDisabled(!valid);
25707             }
25708         }
25709         this.fireEvent('clientvalidation', this, valid);
25710     }
25711     
25712     
25713     
25714     
25715     
25716     
25717     
25718     
25719 });
25720
25721
25722 // back compat
25723 Roo.Form = Roo.form.Form;
25724 /*
25725  * Based on:
25726  * Ext JS Library 1.1.1
25727  * Copyright(c) 2006-2007, Ext JS, LLC.
25728  *
25729  * Originally Released Under LGPL - original licence link has changed is not relivant.
25730  *
25731  * Fork - LGPL
25732  * <script type="text/javascript">
25733  */
25734
25735 // as we use this in bootstrap.
25736 Roo.namespace('Roo.form');
25737  /**
25738  * @class Roo.form.Action
25739  * Internal Class used to handle form actions
25740  * @constructor
25741  * @param {Roo.form.BasicForm} el The form element or its id
25742  * @param {Object} config Configuration options
25743  */
25744
25745  
25746  
25747 // define the action interface
25748 Roo.form.Action = function(form, options){
25749     this.form = form;
25750     this.options = options || {};
25751 };
25752 /**
25753  * Client Validation Failed
25754  * @const 
25755  */
25756 Roo.form.Action.CLIENT_INVALID = 'client';
25757 /**
25758  * Server Validation Failed
25759  * @const 
25760  */
25761 Roo.form.Action.SERVER_INVALID = 'server';
25762  /**
25763  * Connect to Server Failed
25764  * @const 
25765  */
25766 Roo.form.Action.CONNECT_FAILURE = 'connect';
25767 /**
25768  * Reading Data from Server Failed
25769  * @const 
25770  */
25771 Roo.form.Action.LOAD_FAILURE = 'load';
25772
25773 Roo.form.Action.prototype = {
25774     type : 'default',
25775     failureType : undefined,
25776     response : undefined,
25777     result : undefined,
25778
25779     // interface method
25780     run : function(options){
25781
25782     },
25783
25784     // interface method
25785     success : function(response){
25786
25787     },
25788
25789     // interface method
25790     handleResponse : function(response){
25791
25792     },
25793
25794     // default connection failure
25795     failure : function(response){
25796         
25797         this.response = response;
25798         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25799         this.form.afterAction(this, false);
25800     },
25801
25802     processResponse : function(response){
25803         this.response = response;
25804         if(!response.responseText){
25805             return true;
25806         }
25807         this.result = this.handleResponse(response);
25808         return this.result;
25809     },
25810
25811     // utility functions used internally
25812     getUrl : function(appendParams){
25813         var url = this.options.url || this.form.url || this.form.el.dom.action;
25814         if(appendParams){
25815             var p = this.getParams();
25816             if(p){
25817                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25818             }
25819         }
25820         return url;
25821     },
25822
25823     getMethod : function(){
25824         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25825     },
25826
25827     getParams : function(){
25828         var bp = this.form.baseParams;
25829         var p = this.options.params;
25830         if(p){
25831             if(typeof p == "object"){
25832                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25833             }else if(typeof p == 'string' && bp){
25834                 p += '&' + Roo.urlEncode(bp);
25835             }
25836         }else if(bp){
25837             p = Roo.urlEncode(bp);
25838         }
25839         return p;
25840     },
25841
25842     createCallback : function(){
25843         return {
25844             success: this.success,
25845             failure: this.failure,
25846             scope: this,
25847             timeout: (this.form.timeout*1000),
25848             upload: this.form.fileUpload ? this.success : undefined
25849         };
25850     }
25851 };
25852
25853 Roo.form.Action.Submit = function(form, options){
25854     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25855 };
25856
25857 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25858     type : 'submit',
25859
25860     haveProgress : false,
25861     uploadComplete : false,
25862     
25863     // uploadProgress indicator.
25864     uploadProgress : function()
25865     {
25866         if (!this.form.progressUrl) {
25867             return;
25868         }
25869         
25870         if (!this.haveProgress) {
25871             Roo.MessageBox.progress("Uploading", "Uploading");
25872         }
25873         if (this.uploadComplete) {
25874            Roo.MessageBox.hide();
25875            return;
25876         }
25877         
25878         this.haveProgress = true;
25879    
25880         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25881         
25882         var c = new Roo.data.Connection();
25883         c.request({
25884             url : this.form.progressUrl,
25885             params: {
25886                 id : uid
25887             },
25888             method: 'GET',
25889             success : function(req){
25890                //console.log(data);
25891                 var rdata = false;
25892                 var edata;
25893                 try  {
25894                    rdata = Roo.decode(req.responseText)
25895                 } catch (e) {
25896                     Roo.log("Invalid data from server..");
25897                     Roo.log(edata);
25898                     return;
25899                 }
25900                 if (!rdata || !rdata.success) {
25901                     Roo.log(rdata);
25902                     Roo.MessageBox.alert(Roo.encode(rdata));
25903                     return;
25904                 }
25905                 var data = rdata.data;
25906                 
25907                 if (this.uploadComplete) {
25908                    Roo.MessageBox.hide();
25909                    return;
25910                 }
25911                    
25912                 if (data){
25913                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25914                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25915                     );
25916                 }
25917                 this.uploadProgress.defer(2000,this);
25918             },
25919        
25920             failure: function(data) {
25921                 Roo.log('progress url failed ');
25922                 Roo.log(data);
25923             },
25924             scope : this
25925         });
25926            
25927     },
25928     
25929     
25930     run : function()
25931     {
25932         // run get Values on the form, so it syncs any secondary forms.
25933         this.form.getValues();
25934         
25935         var o = this.options;
25936         var method = this.getMethod();
25937         var isPost = method == 'POST';
25938         if(o.clientValidation === false || this.form.isValid()){
25939             
25940             if (this.form.progressUrl) {
25941                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25942                     (new Date() * 1) + '' + Math.random());
25943                     
25944             } 
25945             
25946             
25947             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25948                 form:this.form.el.dom,
25949                 url:this.getUrl(!isPost),
25950                 method: method,
25951                 params:isPost ? this.getParams() : null,
25952                 isUpload: this.form.fileUpload,
25953                 formData : this.form.formData
25954             }));
25955             
25956             this.uploadProgress();
25957
25958         }else if (o.clientValidation !== false){ // client validation failed
25959             this.failureType = Roo.form.Action.CLIENT_INVALID;
25960             this.form.afterAction(this, false);
25961         }
25962     },
25963
25964     success : function(response)
25965     {
25966         this.uploadComplete= true;
25967         if (this.haveProgress) {
25968             Roo.MessageBox.hide();
25969         }
25970         
25971         
25972         var result = this.processResponse(response);
25973         if(result === true || result.success){
25974             this.form.afterAction(this, true);
25975             return;
25976         }
25977         if(result.errors){
25978             this.form.markInvalid(result.errors);
25979             this.failureType = Roo.form.Action.SERVER_INVALID;
25980         }
25981         this.form.afterAction(this, false);
25982     },
25983     failure : function(response)
25984     {
25985         this.uploadComplete= true;
25986         if (this.haveProgress) {
25987             Roo.MessageBox.hide();
25988         }
25989         
25990         this.response = response;
25991         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25992         this.form.afterAction(this, false);
25993     },
25994     
25995     handleResponse : function(response){
25996         if(this.form.errorReader){
25997             var rs = this.form.errorReader.read(response);
25998             var errors = [];
25999             if(rs.records){
26000                 for(var i = 0, len = rs.records.length; i < len; i++) {
26001                     var r = rs.records[i];
26002                     errors[i] = r.data;
26003                 }
26004             }
26005             if(errors.length < 1){
26006                 errors = null;
26007             }
26008             return {
26009                 success : rs.success,
26010                 errors : errors
26011             };
26012         }
26013         var ret = false;
26014         try {
26015             ret = Roo.decode(response.responseText);
26016         } catch (e) {
26017             ret = {
26018                 success: false,
26019                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26020                 errors : []
26021             };
26022         }
26023         return ret;
26024         
26025     }
26026 });
26027
26028
26029 Roo.form.Action.Load = function(form, options){
26030     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26031     this.reader = this.form.reader;
26032 };
26033
26034 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26035     type : 'load',
26036
26037     run : function(){
26038         
26039         Roo.Ajax.request(Roo.apply(
26040                 this.createCallback(), {
26041                     method:this.getMethod(),
26042                     url:this.getUrl(false),
26043                     params:this.getParams()
26044         }));
26045     },
26046
26047     success : function(response){
26048         
26049         var result = this.processResponse(response);
26050         if(result === true || !result.success || !result.data){
26051             this.failureType = Roo.form.Action.LOAD_FAILURE;
26052             this.form.afterAction(this, false);
26053             return;
26054         }
26055         this.form.clearInvalid();
26056         this.form.setValues(result.data);
26057         this.form.afterAction(this, true);
26058     },
26059
26060     handleResponse : function(response){
26061         if(this.form.reader){
26062             var rs = this.form.reader.read(response);
26063             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26064             return {
26065                 success : rs.success,
26066                 data : data
26067             };
26068         }
26069         return Roo.decode(response.responseText);
26070     }
26071 });
26072
26073 Roo.form.Action.ACTION_TYPES = {
26074     'load' : Roo.form.Action.Load,
26075     'submit' : Roo.form.Action.Submit
26076 };/*
26077  * Based on:
26078  * Ext JS Library 1.1.1
26079  * Copyright(c) 2006-2007, Ext JS, LLC.
26080  *
26081  * Originally Released Under LGPL - original licence link has changed is not relivant.
26082  *
26083  * Fork - LGPL
26084  * <script type="text/javascript">
26085  */
26086  
26087 /**
26088  * @class Roo.form.Layout
26089  * @extends Roo.Component
26090  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26091  * @constructor
26092  * @param {Object} config Configuration options
26093  */
26094 Roo.form.Layout = function(config){
26095     var xitems = [];
26096     if (config.items) {
26097         xitems = config.items;
26098         delete config.items;
26099     }
26100     Roo.form.Layout.superclass.constructor.call(this, config);
26101     this.stack = [];
26102     Roo.each(xitems, this.addxtype, this);
26103      
26104 };
26105
26106 Roo.extend(Roo.form.Layout, Roo.Component, {
26107     /**
26108      * @cfg {String/Object} autoCreate
26109      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26110      */
26111     /**
26112      * @cfg {String/Object/Function} style
26113      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26114      * a function which returns such a specification.
26115      */
26116     /**
26117      * @cfg {String} labelAlign
26118      * Valid values are "left," "top" and "right" (defaults to "left")
26119      */
26120     /**
26121      * @cfg {Number} labelWidth
26122      * Fixed width in pixels of all field labels (defaults to undefined)
26123      */
26124     /**
26125      * @cfg {Boolean} clear
26126      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26127      */
26128     clear : true,
26129     /**
26130      * @cfg {String} labelSeparator
26131      * The separator to use after field labels (defaults to ':')
26132      */
26133     labelSeparator : ':',
26134     /**
26135      * @cfg {Boolean} hideLabels
26136      * True to suppress the display of field labels in this layout (defaults to false)
26137      */
26138     hideLabels : false,
26139
26140     // private
26141     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26142     
26143     isLayout : true,
26144     
26145     // private
26146     onRender : function(ct, position){
26147         if(this.el){ // from markup
26148             this.el = Roo.get(this.el);
26149         }else {  // generate
26150             var cfg = this.getAutoCreate();
26151             this.el = ct.createChild(cfg, position);
26152         }
26153         if(this.style){
26154             this.el.applyStyles(this.style);
26155         }
26156         if(this.labelAlign){
26157             this.el.addClass('x-form-label-'+this.labelAlign);
26158         }
26159         if(this.hideLabels){
26160             this.labelStyle = "display:none";
26161             this.elementStyle = "padding-left:0;";
26162         }else{
26163             if(typeof this.labelWidth == 'number'){
26164                 this.labelStyle = "width:"+this.labelWidth+"px;";
26165                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26166             }
26167             if(this.labelAlign == 'top'){
26168                 this.labelStyle = "width:auto;";
26169                 this.elementStyle = "padding-left:0;";
26170             }
26171         }
26172         var stack = this.stack;
26173         var slen = stack.length;
26174         if(slen > 0){
26175             if(!this.fieldTpl){
26176                 var t = new Roo.Template(
26177                     '<div class="x-form-item {5}">',
26178                         '<label for="{0}" style="{2}">{1}{4}</label>',
26179                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26180                         '</div>',
26181                     '</div><div class="x-form-clear-left"></div>'
26182                 );
26183                 t.disableFormats = true;
26184                 t.compile();
26185                 Roo.form.Layout.prototype.fieldTpl = t;
26186             }
26187             for(var i = 0; i < slen; i++) {
26188                 if(stack[i].isFormField){
26189                     this.renderField(stack[i]);
26190                 }else{
26191                     this.renderComponent(stack[i]);
26192                 }
26193             }
26194         }
26195         if(this.clear){
26196             this.el.createChild({cls:'x-form-clear'});
26197         }
26198     },
26199
26200     // private
26201     renderField : function(f){
26202         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26203                f.id, //0
26204                f.fieldLabel, //1
26205                f.labelStyle||this.labelStyle||'', //2
26206                this.elementStyle||'', //3
26207                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26208                f.itemCls||this.itemCls||''  //5
26209        ], true).getPrevSibling());
26210     },
26211
26212     // private
26213     renderComponent : function(c){
26214         c.render(c.isLayout ? this.el : this.el.createChild());    
26215     },
26216     /**
26217      * Adds a object form elements (using the xtype property as the factory method.)
26218      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26219      * @param {Object} config 
26220      */
26221     addxtype : function(o)
26222     {
26223         // create the lement.
26224         o.form = this.form;
26225         var fe = Roo.factory(o, Roo.form);
26226         this.form.allItems.push(fe);
26227         this.stack.push(fe);
26228         
26229         if (fe.isFormField) {
26230             this.form.items.add(fe);
26231         }
26232          
26233         return fe;
26234     }
26235 });
26236
26237 /**
26238  * @class Roo.form.Column
26239  * @extends Roo.form.Layout
26240  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26241  * @constructor
26242  * @param {Object} config Configuration options
26243  */
26244 Roo.form.Column = function(config){
26245     Roo.form.Column.superclass.constructor.call(this, config);
26246 };
26247
26248 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26249     /**
26250      * @cfg {Number/String} width
26251      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26252      */
26253     /**
26254      * @cfg {String/Object} autoCreate
26255      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26256      */
26257
26258     // private
26259     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26260
26261     // private
26262     onRender : function(ct, position){
26263         Roo.form.Column.superclass.onRender.call(this, ct, position);
26264         if(this.width){
26265             this.el.setWidth(this.width);
26266         }
26267     }
26268 });
26269
26270
26271 /**
26272  * @class Roo.form.Row
26273  * @extends Roo.form.Layout
26274  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26275  * @constructor
26276  * @param {Object} config Configuration options
26277  */
26278
26279  
26280 Roo.form.Row = function(config){
26281     Roo.form.Row.superclass.constructor.call(this, config);
26282 };
26283  
26284 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26285       /**
26286      * @cfg {Number/String} width
26287      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26288      */
26289     /**
26290      * @cfg {Number/String} height
26291      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26292      */
26293     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26294     
26295     padWidth : 20,
26296     // private
26297     onRender : function(ct, position){
26298         //console.log('row render');
26299         if(!this.rowTpl){
26300             var t = new Roo.Template(
26301                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26302                     '<label for="{0}" style="{2}">{1}{4}</label>',
26303                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26304                     '</div>',
26305                 '</div>'
26306             );
26307             t.disableFormats = true;
26308             t.compile();
26309             Roo.form.Layout.prototype.rowTpl = t;
26310         }
26311         this.fieldTpl = this.rowTpl;
26312         
26313         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26314         var labelWidth = 100;
26315         
26316         if ((this.labelAlign != 'top')) {
26317             if (typeof this.labelWidth == 'number') {
26318                 labelWidth = this.labelWidth
26319             }
26320             this.padWidth =  20 + labelWidth;
26321             
26322         }
26323         
26324         Roo.form.Column.superclass.onRender.call(this, ct, position);
26325         if(this.width){
26326             this.el.setWidth(this.width);
26327         }
26328         if(this.height){
26329             this.el.setHeight(this.height);
26330         }
26331     },
26332     
26333     // private
26334     renderField : function(f){
26335         f.fieldEl = this.fieldTpl.append(this.el, [
26336                f.id, f.fieldLabel,
26337                f.labelStyle||this.labelStyle||'',
26338                this.elementStyle||'',
26339                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26340                f.itemCls||this.itemCls||'',
26341                f.width ? f.width + this.padWidth : 160 + this.padWidth
26342        ],true);
26343     }
26344 });
26345  
26346
26347 /**
26348  * @class Roo.form.FieldSet
26349  * @extends Roo.form.Layout
26350  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26351  * @constructor
26352  * @param {Object} config Configuration options
26353  */
26354 Roo.form.FieldSet = function(config){
26355     Roo.form.FieldSet.superclass.constructor.call(this, config);
26356 };
26357
26358 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26359     /**
26360      * @cfg {String} legend
26361      * The text to display as the legend for the FieldSet (defaults to '')
26362      */
26363     /**
26364      * @cfg {String/Object} autoCreate
26365      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26366      */
26367
26368     // private
26369     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26370
26371     // private
26372     onRender : function(ct, position){
26373         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26374         if(this.legend){
26375             this.setLegend(this.legend);
26376         }
26377     },
26378
26379     // private
26380     setLegend : function(text){
26381         if(this.rendered){
26382             this.el.child('legend').update(text);
26383         }
26384     }
26385 });/*
26386  * Based on:
26387  * Ext JS Library 1.1.1
26388  * Copyright(c) 2006-2007, Ext JS, LLC.
26389  *
26390  * Originally Released Under LGPL - original licence link has changed is not relivant.
26391  *
26392  * Fork - LGPL
26393  * <script type="text/javascript">
26394  */
26395 /**
26396  * @class Roo.form.VTypes
26397  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26398  * @singleton
26399  */
26400 Roo.form.VTypes = function(){
26401     // closure these in so they are only created once.
26402     var alpha = /^[a-zA-Z_]+$/;
26403     var alphanum = /^[a-zA-Z0-9_]+$/;
26404     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26405     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26406
26407     // All these messages and functions are configurable
26408     return {
26409         /**
26410          * The function used to validate email addresses
26411          * @param {String} value The email address
26412          */
26413         'email' : function(v){
26414             return email.test(v);
26415         },
26416         /**
26417          * The error text to display when the email validation function returns false
26418          * @type String
26419          */
26420         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26421         /**
26422          * The keystroke filter mask to be applied on email input
26423          * @type RegExp
26424          */
26425         'emailMask' : /[a-z0-9_\.\-@]/i,
26426
26427         /**
26428          * The function used to validate URLs
26429          * @param {String} value The URL
26430          */
26431         'url' : function(v){
26432             return url.test(v);
26433         },
26434         /**
26435          * The error text to display when the url validation function returns false
26436          * @type String
26437          */
26438         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26439         
26440         /**
26441          * The function used to validate alpha values
26442          * @param {String} value The value
26443          */
26444         'alpha' : function(v){
26445             return alpha.test(v);
26446         },
26447         /**
26448          * The error text to display when the alpha validation function returns false
26449          * @type String
26450          */
26451         'alphaText' : 'This field should only contain letters and _',
26452         /**
26453          * The keystroke filter mask to be applied on alpha input
26454          * @type RegExp
26455          */
26456         'alphaMask' : /[a-z_]/i,
26457
26458         /**
26459          * The function used to validate alphanumeric values
26460          * @param {String} value The value
26461          */
26462         'alphanum' : function(v){
26463             return alphanum.test(v);
26464         },
26465         /**
26466          * The error text to display when the alphanumeric validation function returns false
26467          * @type String
26468          */
26469         'alphanumText' : 'This field should only contain letters, numbers and _',
26470         /**
26471          * The keystroke filter mask to be applied on alphanumeric input
26472          * @type RegExp
26473          */
26474         'alphanumMask' : /[a-z0-9_]/i
26475     };
26476 }();//<script type="text/javascript">
26477
26478 /**
26479  * @class Roo.form.FCKeditor
26480  * @extends Roo.form.TextArea
26481  * Wrapper around the FCKEditor http://www.fckeditor.net
26482  * @constructor
26483  * Creates a new FCKeditor
26484  * @param {Object} config Configuration options
26485  */
26486 Roo.form.FCKeditor = function(config){
26487     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26488     this.addEvents({
26489          /**
26490          * @event editorinit
26491          * Fired when the editor is initialized - you can add extra handlers here..
26492          * @param {FCKeditor} this
26493          * @param {Object} the FCK object.
26494          */
26495         editorinit : true
26496     });
26497     
26498     
26499 };
26500 Roo.form.FCKeditor.editors = { };
26501 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26502 {
26503     //defaultAutoCreate : {
26504     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26505     //},
26506     // private
26507     /**
26508      * @cfg {Object} fck options - see fck manual for details.
26509      */
26510     fckconfig : false,
26511     
26512     /**
26513      * @cfg {Object} fck toolbar set (Basic or Default)
26514      */
26515     toolbarSet : 'Basic',
26516     /**
26517      * @cfg {Object} fck BasePath
26518      */ 
26519     basePath : '/fckeditor/',
26520     
26521     
26522     frame : false,
26523     
26524     value : '',
26525     
26526    
26527     onRender : function(ct, position)
26528     {
26529         if(!this.el){
26530             this.defaultAutoCreate = {
26531                 tag: "textarea",
26532                 style:"width:300px;height:60px;",
26533                 autocomplete: "new-password"
26534             };
26535         }
26536         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26537         /*
26538         if(this.grow){
26539             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26540             if(this.preventScrollbars){
26541                 this.el.setStyle("overflow", "hidden");
26542             }
26543             this.el.setHeight(this.growMin);
26544         }
26545         */
26546         //console.log('onrender' + this.getId() );
26547         Roo.form.FCKeditor.editors[this.getId()] = this;
26548          
26549
26550         this.replaceTextarea() ;
26551         
26552     },
26553     
26554     getEditor : function() {
26555         return this.fckEditor;
26556     },
26557     /**
26558      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26559      * @param {Mixed} value The value to set
26560      */
26561     
26562     
26563     setValue : function(value)
26564     {
26565         //console.log('setValue: ' + value);
26566         
26567         if(typeof(value) == 'undefined') { // not sure why this is happending...
26568             return;
26569         }
26570         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26571         
26572         //if(!this.el || !this.getEditor()) {
26573         //    this.value = value;
26574             //this.setValue.defer(100,this,[value]);    
26575         //    return;
26576         //} 
26577         
26578         if(!this.getEditor()) {
26579             return;
26580         }
26581         
26582         this.getEditor().SetData(value);
26583         
26584         //
26585
26586     },
26587
26588     /**
26589      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26590      * @return {Mixed} value The field value
26591      */
26592     getValue : function()
26593     {
26594         
26595         if (this.frame && this.frame.dom.style.display == 'none') {
26596             return Roo.form.FCKeditor.superclass.getValue.call(this);
26597         }
26598         
26599         if(!this.el || !this.getEditor()) {
26600            
26601            // this.getValue.defer(100,this); 
26602             return this.value;
26603         }
26604        
26605         
26606         var value=this.getEditor().GetData();
26607         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26608         return Roo.form.FCKeditor.superclass.getValue.call(this);
26609         
26610
26611     },
26612
26613     /**
26614      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26615      * @return {Mixed} value The field value
26616      */
26617     getRawValue : function()
26618     {
26619         if (this.frame && this.frame.dom.style.display == 'none') {
26620             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26621         }
26622         
26623         if(!this.el || !this.getEditor()) {
26624             //this.getRawValue.defer(100,this); 
26625             return this.value;
26626             return;
26627         }
26628         
26629         
26630         
26631         var value=this.getEditor().GetData();
26632         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26633         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26634          
26635     },
26636     
26637     setSize : function(w,h) {
26638         
26639         
26640         
26641         //if (this.frame && this.frame.dom.style.display == 'none') {
26642         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26643         //    return;
26644         //}
26645         //if(!this.el || !this.getEditor()) {
26646         //    this.setSize.defer(100,this, [w,h]); 
26647         //    return;
26648         //}
26649         
26650         
26651         
26652         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26653         
26654         this.frame.dom.setAttribute('width', w);
26655         this.frame.dom.setAttribute('height', h);
26656         this.frame.setSize(w,h);
26657         
26658     },
26659     
26660     toggleSourceEdit : function(value) {
26661         
26662       
26663          
26664         this.el.dom.style.display = value ? '' : 'none';
26665         this.frame.dom.style.display = value ?  'none' : '';
26666         
26667     },
26668     
26669     
26670     focus: function(tag)
26671     {
26672         if (this.frame.dom.style.display == 'none') {
26673             return Roo.form.FCKeditor.superclass.focus.call(this);
26674         }
26675         if(!this.el || !this.getEditor()) {
26676             this.focus.defer(100,this, [tag]); 
26677             return;
26678         }
26679         
26680         
26681         
26682         
26683         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26684         this.getEditor().Focus();
26685         if (tgs.length) {
26686             if (!this.getEditor().Selection.GetSelection()) {
26687                 this.focus.defer(100,this, [tag]); 
26688                 return;
26689             }
26690             
26691             
26692             var r = this.getEditor().EditorDocument.createRange();
26693             r.setStart(tgs[0],0);
26694             r.setEnd(tgs[0],0);
26695             this.getEditor().Selection.GetSelection().removeAllRanges();
26696             this.getEditor().Selection.GetSelection().addRange(r);
26697             this.getEditor().Focus();
26698         }
26699         
26700     },
26701     
26702     
26703     
26704     replaceTextarea : function()
26705     {
26706         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26707             return ;
26708         }
26709         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26710         //{
26711             // We must check the elements firstly using the Id and then the name.
26712         var oTextarea = document.getElementById( this.getId() );
26713         
26714         var colElementsByName = document.getElementsByName( this.getId() ) ;
26715          
26716         oTextarea.style.display = 'none' ;
26717
26718         if ( oTextarea.tabIndex ) {            
26719             this.TabIndex = oTextarea.tabIndex ;
26720         }
26721         
26722         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26723         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26724         this.frame = Roo.get(this.getId() + '___Frame')
26725     },
26726     
26727     _getConfigHtml : function()
26728     {
26729         var sConfig = '' ;
26730
26731         for ( var o in this.fckconfig ) {
26732             sConfig += sConfig.length > 0  ? '&amp;' : '';
26733             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26734         }
26735
26736         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26737     },
26738     
26739     
26740     _getIFrameHtml : function()
26741     {
26742         var sFile = 'fckeditor.html' ;
26743         /* no idea what this is about..
26744         try
26745         {
26746             if ( (/fcksource=true/i).test( window.top.location.search ) )
26747                 sFile = 'fckeditor.original.html' ;
26748         }
26749         catch (e) { 
26750         */
26751
26752         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26753         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26754         
26755         
26756         var html = '<iframe id="' + this.getId() +
26757             '___Frame" src="' + sLink +
26758             '" width="' + this.width +
26759             '" height="' + this.height + '"' +
26760             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26761             ' frameborder="0" scrolling="no"></iframe>' ;
26762
26763         return html ;
26764     },
26765     
26766     _insertHtmlBefore : function( html, element )
26767     {
26768         if ( element.insertAdjacentHTML )       {
26769             // IE
26770             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26771         } else { // Gecko
26772             var oRange = document.createRange() ;
26773             oRange.setStartBefore( element ) ;
26774             var oFragment = oRange.createContextualFragment( html );
26775             element.parentNode.insertBefore( oFragment, element ) ;
26776         }
26777     }
26778     
26779     
26780   
26781     
26782     
26783     
26784     
26785
26786 });
26787
26788 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26789
26790 function FCKeditor_OnComplete(editorInstance){
26791     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26792     f.fckEditor = editorInstance;
26793     //console.log("loaded");
26794     f.fireEvent('editorinit', f, editorInstance);
26795
26796   
26797
26798  
26799
26800
26801
26802
26803
26804
26805
26806
26807
26808
26809
26810
26811
26812
26813
26814 //<script type="text/javascript">
26815 /**
26816  * @class Roo.form.GridField
26817  * @extends Roo.form.Field
26818  * Embed a grid (or editable grid into a form)
26819  * STATUS ALPHA
26820  * 
26821  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26822  * it needs 
26823  * xgrid.store = Roo.data.Store
26824  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26825  * xgrid.store.reader = Roo.data.JsonReader 
26826  * 
26827  * 
26828  * @constructor
26829  * Creates a new GridField
26830  * @param {Object} config Configuration options
26831  */
26832 Roo.form.GridField = function(config){
26833     Roo.form.GridField.superclass.constructor.call(this, config);
26834      
26835 };
26836
26837 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26838     /**
26839      * @cfg {Number} width  - used to restrict width of grid..
26840      */
26841     width : 100,
26842     /**
26843      * @cfg {Number} height - used to restrict height of grid..
26844      */
26845     height : 50,
26846      /**
26847      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26848          * 
26849          *}
26850      */
26851     xgrid : false, 
26852     /**
26853      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26854      * {tag: "input", type: "checkbox", autocomplete: "off"})
26855      */
26856    // defaultAutoCreate : { tag: 'div' },
26857     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26858     /**
26859      * @cfg {String} addTitle Text to include for adding a title.
26860      */
26861     addTitle : false,
26862     //
26863     onResize : function(){
26864         Roo.form.Field.superclass.onResize.apply(this, arguments);
26865     },
26866
26867     initEvents : function(){
26868         // Roo.form.Checkbox.superclass.initEvents.call(this);
26869         // has no events...
26870        
26871     },
26872
26873
26874     getResizeEl : function(){
26875         return this.wrap;
26876     },
26877
26878     getPositionEl : function(){
26879         return this.wrap;
26880     },
26881
26882     // private
26883     onRender : function(ct, position){
26884         
26885         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26886         var style = this.style;
26887         delete this.style;
26888         
26889         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26890         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26891         this.viewEl = this.wrap.createChild({ tag: 'div' });
26892         if (style) {
26893             this.viewEl.applyStyles(style);
26894         }
26895         if (this.width) {
26896             this.viewEl.setWidth(this.width);
26897         }
26898         if (this.height) {
26899             this.viewEl.setHeight(this.height);
26900         }
26901         //if(this.inputValue !== undefined){
26902         //this.setValue(this.value);
26903         
26904         
26905         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26906         
26907         
26908         this.grid.render();
26909         this.grid.getDataSource().on('remove', this.refreshValue, this);
26910         this.grid.getDataSource().on('update', this.refreshValue, this);
26911         this.grid.on('afteredit', this.refreshValue, this);
26912  
26913     },
26914      
26915     
26916     /**
26917      * Sets the value of the item. 
26918      * @param {String} either an object  or a string..
26919      */
26920     setValue : function(v){
26921         //this.value = v;
26922         v = v || []; // empty set..
26923         // this does not seem smart - it really only affects memoryproxy grids..
26924         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26925             var ds = this.grid.getDataSource();
26926             // assumes a json reader..
26927             var data = {}
26928             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26929             ds.loadData( data);
26930         }
26931         // clear selection so it does not get stale.
26932         if (this.grid.sm) { 
26933             this.grid.sm.clearSelections();
26934         }
26935         
26936         Roo.form.GridField.superclass.setValue.call(this, v);
26937         this.refreshValue();
26938         // should load data in the grid really....
26939     },
26940     
26941     // private
26942     refreshValue: function() {
26943          var val = [];
26944         this.grid.getDataSource().each(function(r) {
26945             val.push(r.data);
26946         });
26947         this.el.dom.value = Roo.encode(val);
26948     }
26949     
26950      
26951     
26952     
26953 });/*
26954  * Based on:
26955  * Ext JS Library 1.1.1
26956  * Copyright(c) 2006-2007, Ext JS, LLC.
26957  *
26958  * Originally Released Under LGPL - original licence link has changed is not relivant.
26959  *
26960  * Fork - LGPL
26961  * <script type="text/javascript">
26962  */
26963 /**
26964  * @class Roo.form.DisplayField
26965  * @extends Roo.form.Field
26966  * A generic Field to display non-editable data.
26967  * @cfg {Boolean} closable (true|false) default false
26968  * @constructor
26969  * Creates a new Display Field item.
26970  * @param {Object} config Configuration options
26971  */
26972 Roo.form.DisplayField = function(config){
26973     Roo.form.DisplayField.superclass.constructor.call(this, config);
26974     
26975     this.addEvents({
26976         /**
26977          * @event close
26978          * Fires after the click the close btn
26979              * @param {Roo.form.DisplayField} this
26980              */
26981         close : true
26982     });
26983 };
26984
26985 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26986     inputType:      'hidden',
26987     allowBlank:     true,
26988     readOnly:         true,
26989     
26990  
26991     /**
26992      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26993      */
26994     focusClass : undefined,
26995     /**
26996      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26997      */
26998     fieldClass: 'x-form-field',
26999     
27000      /**
27001      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27002      */
27003     valueRenderer: undefined,
27004     
27005     width: 100,
27006     /**
27007      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27008      * {tag: "input", type: "checkbox", autocomplete: "off"})
27009      */
27010      
27011  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27012  
27013     closable : false,
27014     
27015     onResize : function(){
27016         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27017         
27018     },
27019
27020     initEvents : function(){
27021         // Roo.form.Checkbox.superclass.initEvents.call(this);
27022         // has no events...
27023         
27024         if(this.closable){
27025             this.closeEl.on('click', this.onClose, this);
27026         }
27027        
27028     },
27029
27030
27031     getResizeEl : function(){
27032         return this.wrap;
27033     },
27034
27035     getPositionEl : function(){
27036         return this.wrap;
27037     },
27038
27039     // private
27040     onRender : function(ct, position){
27041         
27042         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27043         //if(this.inputValue !== undefined){
27044         this.wrap = this.el.wrap();
27045         
27046         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27047         
27048         if(this.closable){
27049             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27050         }
27051         
27052         if (this.bodyStyle) {
27053             this.viewEl.applyStyles(this.bodyStyle);
27054         }
27055         //this.viewEl.setStyle('padding', '2px');
27056         
27057         this.setValue(this.value);
27058         
27059     },
27060 /*
27061     // private
27062     initValue : Roo.emptyFn,
27063
27064   */
27065
27066         // private
27067     onClick : function(){
27068         
27069     },
27070
27071     /**
27072      * Sets the checked state of the checkbox.
27073      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27074      */
27075     setValue : function(v){
27076         this.value = v;
27077         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27078         // this might be called before we have a dom element..
27079         if (!this.viewEl) {
27080             return;
27081         }
27082         this.viewEl.dom.innerHTML = html;
27083         Roo.form.DisplayField.superclass.setValue.call(this, v);
27084
27085     },
27086     
27087     onClose : function(e)
27088     {
27089         e.preventDefault();
27090         
27091         this.fireEvent('close', this);
27092     }
27093 });/*
27094  * 
27095  * Licence- LGPL
27096  * 
27097  */
27098
27099 /**
27100  * @class Roo.form.DayPicker
27101  * @extends Roo.form.Field
27102  * A Day picker show [M] [T] [W] ....
27103  * @constructor
27104  * Creates a new Day Picker
27105  * @param {Object} config Configuration options
27106  */
27107 Roo.form.DayPicker= function(config){
27108     Roo.form.DayPicker.superclass.constructor.call(this, config);
27109      
27110 };
27111
27112 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27113     /**
27114      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27115      */
27116     focusClass : undefined,
27117     /**
27118      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27119      */
27120     fieldClass: "x-form-field",
27121    
27122     /**
27123      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27124      * {tag: "input", type: "checkbox", autocomplete: "off"})
27125      */
27126     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27127     
27128    
27129     actionMode : 'viewEl', 
27130     //
27131     // private
27132  
27133     inputType : 'hidden',
27134     
27135      
27136     inputElement: false, // real input element?
27137     basedOn: false, // ????
27138     
27139     isFormField: true, // not sure where this is needed!!!!
27140
27141     onResize : function(){
27142         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27143         if(!this.boxLabel){
27144             this.el.alignTo(this.wrap, 'c-c');
27145         }
27146     },
27147
27148     initEvents : function(){
27149         Roo.form.Checkbox.superclass.initEvents.call(this);
27150         this.el.on("click", this.onClick,  this);
27151         this.el.on("change", this.onClick,  this);
27152     },
27153
27154
27155     getResizeEl : function(){
27156         return this.wrap;
27157     },
27158
27159     getPositionEl : function(){
27160         return this.wrap;
27161     },
27162
27163     
27164     // private
27165     onRender : function(ct, position){
27166         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27167        
27168         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27169         
27170         var r1 = '<table><tr>';
27171         var r2 = '<tr class="x-form-daypick-icons">';
27172         for (var i=0; i < 7; i++) {
27173             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27174             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27175         }
27176         
27177         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27178         viewEl.select('img').on('click', this.onClick, this);
27179         this.viewEl = viewEl;   
27180         
27181         
27182         // this will not work on Chrome!!!
27183         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27184         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27185         
27186         
27187           
27188
27189     },
27190
27191     // private
27192     initValue : Roo.emptyFn,
27193
27194     /**
27195      * Returns the checked state of the checkbox.
27196      * @return {Boolean} True if checked, else false
27197      */
27198     getValue : function(){
27199         return this.el.dom.value;
27200         
27201     },
27202
27203         // private
27204     onClick : function(e){ 
27205         //this.setChecked(!this.checked);
27206         Roo.get(e.target).toggleClass('x-menu-item-checked');
27207         this.refreshValue();
27208         //if(this.el.dom.checked != this.checked){
27209         //    this.setValue(this.el.dom.checked);
27210        // }
27211     },
27212     
27213     // private
27214     refreshValue : function()
27215     {
27216         var val = '';
27217         this.viewEl.select('img',true).each(function(e,i,n)  {
27218             val += e.is(".x-menu-item-checked") ? String(n) : '';
27219         });
27220         this.setValue(val, true);
27221     },
27222
27223     /**
27224      * Sets the checked state of the checkbox.
27225      * On is always based on a string comparison between inputValue and the param.
27226      * @param {Boolean/String} value - the value to set 
27227      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27228      */
27229     setValue : function(v,suppressEvent){
27230         if (!this.el.dom) {
27231             return;
27232         }
27233         var old = this.el.dom.value ;
27234         this.el.dom.value = v;
27235         if (suppressEvent) {
27236             return ;
27237         }
27238          
27239         // update display..
27240         this.viewEl.select('img',true).each(function(e,i,n)  {
27241             
27242             var on = e.is(".x-menu-item-checked");
27243             var newv = v.indexOf(String(n)) > -1;
27244             if (on != newv) {
27245                 e.toggleClass('x-menu-item-checked');
27246             }
27247             
27248         });
27249         
27250         
27251         this.fireEvent('change', this, v, old);
27252         
27253         
27254     },
27255    
27256     // handle setting of hidden value by some other method!!?!?
27257     setFromHidden: function()
27258     {
27259         if(!this.el){
27260             return;
27261         }
27262         //console.log("SET FROM HIDDEN");
27263         //alert('setFrom hidden');
27264         this.setValue(this.el.dom.value);
27265     },
27266     
27267     onDestroy : function()
27268     {
27269         if(this.viewEl){
27270             Roo.get(this.viewEl).remove();
27271         }
27272          
27273         Roo.form.DayPicker.superclass.onDestroy.call(this);
27274     }
27275
27276 });/*
27277  * RooJS Library 1.1.1
27278  * Copyright(c) 2008-2011  Alan Knowles
27279  *
27280  * License - LGPL
27281  */
27282  
27283
27284 /**
27285  * @class Roo.form.ComboCheck
27286  * @extends Roo.form.ComboBox
27287  * A combobox for multiple select items.
27288  *
27289  * FIXME - could do with a reset button..
27290  * 
27291  * @constructor
27292  * Create a new ComboCheck
27293  * @param {Object} config Configuration options
27294  */
27295 Roo.form.ComboCheck = function(config){
27296     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27297     // should verify some data...
27298     // like
27299     // hiddenName = required..
27300     // displayField = required
27301     // valudField == required
27302     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27303     var _t = this;
27304     Roo.each(req, function(e) {
27305         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27306             throw "Roo.form.ComboCheck : missing value for: " + e;
27307         }
27308     });
27309     
27310     
27311 };
27312
27313 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27314      
27315      
27316     editable : false,
27317      
27318     selectedClass: 'x-menu-item-checked', 
27319     
27320     // private
27321     onRender : function(ct, position){
27322         var _t = this;
27323         
27324         
27325         
27326         if(!this.tpl){
27327             var cls = 'x-combo-list';
27328
27329             
27330             this.tpl =  new Roo.Template({
27331                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27332                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27333                    '<span>{' + this.displayField + '}</span>' +
27334                     '</div>' 
27335                 
27336             });
27337         }
27338  
27339         
27340         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27341         this.view.singleSelect = false;
27342         this.view.multiSelect = true;
27343         this.view.toggleSelect = true;
27344         this.pageTb.add(new Roo.Toolbar.Fill(), {
27345             
27346             text: 'Done',
27347             handler: function()
27348             {
27349                 _t.collapse();
27350             }
27351         });
27352     },
27353     
27354     onViewOver : function(e, t){
27355         // do nothing...
27356         return;
27357         
27358     },
27359     
27360     onViewClick : function(doFocus,index){
27361         return;
27362         
27363     },
27364     select: function () {
27365         //Roo.log("SELECT CALLED");
27366     },
27367      
27368     selectByValue : function(xv, scrollIntoView){
27369         var ar = this.getValueArray();
27370         var sels = [];
27371         
27372         Roo.each(ar, function(v) {
27373             if(v === undefined || v === null){
27374                 return;
27375             }
27376             var r = this.findRecord(this.valueField, v);
27377             if(r){
27378                 sels.push(this.store.indexOf(r))
27379                 
27380             }
27381         },this);
27382         this.view.select(sels);
27383         return false;
27384     },
27385     
27386     
27387     
27388     onSelect : function(record, index){
27389        // Roo.log("onselect Called");
27390        // this is only called by the clear button now..
27391         this.view.clearSelections();
27392         this.setValue('[]');
27393         if (this.value != this.valueBefore) {
27394             this.fireEvent('change', this, this.value, this.valueBefore);
27395             this.valueBefore = this.value;
27396         }
27397     },
27398     getValueArray : function()
27399     {
27400         var ar = [] ;
27401         
27402         try {
27403             //Roo.log(this.value);
27404             if (typeof(this.value) == 'undefined') {
27405                 return [];
27406             }
27407             var ar = Roo.decode(this.value);
27408             return  ar instanceof Array ? ar : []; //?? valid?
27409             
27410         } catch(e) {
27411             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27412             return [];
27413         }
27414          
27415     },
27416     expand : function ()
27417     {
27418         
27419         Roo.form.ComboCheck.superclass.expand.call(this);
27420         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27421         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27422         
27423
27424     },
27425     
27426     collapse : function(){
27427         Roo.form.ComboCheck.superclass.collapse.call(this);
27428         var sl = this.view.getSelectedIndexes();
27429         var st = this.store;
27430         var nv = [];
27431         var tv = [];
27432         var r;
27433         Roo.each(sl, function(i) {
27434             r = st.getAt(i);
27435             nv.push(r.get(this.valueField));
27436         },this);
27437         this.setValue(Roo.encode(nv));
27438         if (this.value != this.valueBefore) {
27439
27440             this.fireEvent('change', this, this.value, this.valueBefore);
27441             this.valueBefore = this.value;
27442         }
27443         
27444     },
27445     
27446     setValue : function(v){
27447         // Roo.log(v);
27448         this.value = v;
27449         
27450         var vals = this.getValueArray();
27451         var tv = [];
27452         Roo.each(vals, function(k) {
27453             var r = this.findRecord(this.valueField, k);
27454             if(r){
27455                 tv.push(r.data[this.displayField]);
27456             }else if(this.valueNotFoundText !== undefined){
27457                 tv.push( this.valueNotFoundText );
27458             }
27459         },this);
27460        // Roo.log(tv);
27461         
27462         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27463         this.hiddenField.value = v;
27464         this.value = v;
27465     }
27466     
27467 });/*
27468  * Based on:
27469  * Ext JS Library 1.1.1
27470  * Copyright(c) 2006-2007, Ext JS, LLC.
27471  *
27472  * Originally Released Under LGPL - original licence link has changed is not relivant.
27473  *
27474  * Fork - LGPL
27475  * <script type="text/javascript">
27476  */
27477  
27478 /**
27479  * @class Roo.form.Signature
27480  * @extends Roo.form.Field
27481  * Signature field.  
27482  * @constructor
27483  * 
27484  * @param {Object} config Configuration options
27485  */
27486
27487 Roo.form.Signature = function(config){
27488     Roo.form.Signature.superclass.constructor.call(this, config);
27489     
27490     this.addEvents({// not in used??
27491          /**
27492          * @event confirm
27493          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27494              * @param {Roo.form.Signature} combo This combo box
27495              */
27496         'confirm' : true,
27497         /**
27498          * @event reset
27499          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27500              * @param {Roo.form.ComboBox} combo This combo box
27501              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27502              */
27503         'reset' : true
27504     });
27505 };
27506
27507 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27508     /**
27509      * @cfg {Object} labels Label to use when rendering a form.
27510      * defaults to 
27511      * labels : { 
27512      *      clear : "Clear",
27513      *      confirm : "Confirm"
27514      *  }
27515      */
27516     labels : { 
27517         clear : "Clear",
27518         confirm : "Confirm"
27519     },
27520     /**
27521      * @cfg {Number} width The signature panel width (defaults to 300)
27522      */
27523     width: 300,
27524     /**
27525      * @cfg {Number} height The signature panel height (defaults to 100)
27526      */
27527     height : 100,
27528     /**
27529      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27530      */
27531     allowBlank : false,
27532     
27533     //private
27534     // {Object} signPanel The signature SVG panel element (defaults to {})
27535     signPanel : {},
27536     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27537     isMouseDown : false,
27538     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27539     isConfirmed : false,
27540     // {String} signatureTmp SVG mapping string (defaults to empty string)
27541     signatureTmp : '',
27542     
27543     
27544     defaultAutoCreate : { // modified by initCompnoent..
27545         tag: "input",
27546         type:"hidden"
27547     },
27548
27549     // private
27550     onRender : function(ct, position){
27551         
27552         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27553         
27554         this.wrap = this.el.wrap({
27555             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27556         });
27557         
27558         this.createToolbar(this);
27559         this.signPanel = this.wrap.createChild({
27560                 tag: 'div',
27561                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27562             }, this.el
27563         );
27564             
27565         this.svgID = Roo.id();
27566         this.svgEl = this.signPanel.createChild({
27567               xmlns : 'http://www.w3.org/2000/svg',
27568               tag : 'svg',
27569               id : this.svgID + "-svg",
27570               width: this.width,
27571               height: this.height,
27572               viewBox: '0 0 '+this.width+' '+this.height,
27573               cn : [
27574                 {
27575                     tag: "rect",
27576                     id: this.svgID + "-svg-r",
27577                     width: this.width,
27578                     height: this.height,
27579                     fill: "#ffa"
27580                 },
27581                 {
27582                     tag: "line",
27583                     id: this.svgID + "-svg-l",
27584                     x1: "0", // start
27585                     y1: (this.height*0.8), // start set the line in 80% of height
27586                     x2: this.width, // end
27587                     y2: (this.height*0.8), // end set the line in 80% of height
27588                     'stroke': "#666",
27589                     'stroke-width': "1",
27590                     'stroke-dasharray': "3",
27591                     'shape-rendering': "crispEdges",
27592                     'pointer-events': "none"
27593                 },
27594                 {
27595                     tag: "path",
27596                     id: this.svgID + "-svg-p",
27597                     'stroke': "navy",
27598                     'stroke-width': "3",
27599                     'fill': "none",
27600                     'pointer-events': 'none'
27601                 }
27602               ]
27603         });
27604         this.createSVG();
27605         this.svgBox = this.svgEl.dom.getScreenCTM();
27606     },
27607     createSVG : function(){ 
27608         var svg = this.signPanel;
27609         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27610         var t = this;
27611
27612         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27613         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27614         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27615         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27616         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27617         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27618         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27619         
27620     },
27621     isTouchEvent : function(e){
27622         return e.type.match(/^touch/);
27623     },
27624     getCoords : function (e) {
27625         var pt    = this.svgEl.dom.createSVGPoint();
27626         pt.x = e.clientX; 
27627         pt.y = e.clientY;
27628         if (this.isTouchEvent(e)) {
27629             pt.x =  e.targetTouches[0].clientX;
27630             pt.y = e.targetTouches[0].clientY;
27631         }
27632         var a = this.svgEl.dom.getScreenCTM();
27633         var b = a.inverse();
27634         var mx = pt.matrixTransform(b);
27635         return mx.x + ',' + mx.y;
27636     },
27637     //mouse event headler 
27638     down : function (e) {
27639         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27640         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27641         
27642         this.isMouseDown = true;
27643         
27644         e.preventDefault();
27645     },
27646     move : function (e) {
27647         if (this.isMouseDown) {
27648             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27649             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27650         }
27651         
27652         e.preventDefault();
27653     },
27654     up : function (e) {
27655         this.isMouseDown = false;
27656         var sp = this.signatureTmp.split(' ');
27657         
27658         if(sp.length > 1){
27659             if(!sp[sp.length-2].match(/^L/)){
27660                 sp.pop();
27661                 sp.pop();
27662                 sp.push("");
27663                 this.signatureTmp = sp.join(" ");
27664             }
27665         }
27666         if(this.getValue() != this.signatureTmp){
27667             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27668             this.isConfirmed = false;
27669         }
27670         e.preventDefault();
27671     },
27672     
27673     /**
27674      * Protected method that will not generally be called directly. It
27675      * is called when the editor creates its toolbar. Override this method if you need to
27676      * add custom toolbar buttons.
27677      * @param {HtmlEditor} editor
27678      */
27679     createToolbar : function(editor){
27680          function btn(id, toggle, handler){
27681             var xid = fid + '-'+ id ;
27682             return {
27683                 id : xid,
27684                 cmd : id,
27685                 cls : 'x-btn-icon x-edit-'+id,
27686                 enableToggle:toggle !== false,
27687                 scope: editor, // was editor...
27688                 handler:handler||editor.relayBtnCmd,
27689                 clickEvent:'mousedown',
27690                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27691                 tabIndex:-1
27692             };
27693         }
27694         
27695         
27696         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27697         this.tb = tb;
27698         this.tb.add(
27699            {
27700                 cls : ' x-signature-btn x-signature-'+id,
27701                 scope: editor, // was editor...
27702                 handler: this.reset,
27703                 clickEvent:'mousedown',
27704                 text: this.labels.clear
27705             },
27706             {
27707                  xtype : 'Fill',
27708                  xns: Roo.Toolbar
27709             }, 
27710             {
27711                 cls : '  x-signature-btn x-signature-'+id,
27712                 scope: editor, // was editor...
27713                 handler: this.confirmHandler,
27714                 clickEvent:'mousedown',
27715                 text: this.labels.confirm
27716             }
27717         );
27718     
27719     },
27720     //public
27721     /**
27722      * when user is clicked confirm then show this image.....
27723      * 
27724      * @return {String} Image Data URI
27725      */
27726     getImageDataURI : function(){
27727         var svg = this.svgEl.dom.parentNode.innerHTML;
27728         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27729         return src; 
27730     },
27731     /**
27732      * 
27733      * @return {Boolean} this.isConfirmed
27734      */
27735     getConfirmed : function(){
27736         return this.isConfirmed;
27737     },
27738     /**
27739      * 
27740      * @return {Number} this.width
27741      */
27742     getWidth : function(){
27743         return this.width;
27744     },
27745     /**
27746      * 
27747      * @return {Number} this.height
27748      */
27749     getHeight : function(){
27750         return this.height;
27751     },
27752     // private
27753     getSignature : function(){
27754         return this.signatureTmp;
27755     },
27756     // private
27757     reset : function(){
27758         this.signatureTmp = '';
27759         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27760         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27761         this.isConfirmed = false;
27762         Roo.form.Signature.superclass.reset.call(this);
27763     },
27764     setSignature : function(s){
27765         this.signatureTmp = s;
27766         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27767         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27768         this.setValue(s);
27769         this.isConfirmed = false;
27770         Roo.form.Signature.superclass.reset.call(this);
27771     }, 
27772     test : function(){
27773 //        Roo.log(this.signPanel.dom.contentWindow.up())
27774     },
27775     //private
27776     setConfirmed : function(){
27777         
27778         
27779         
27780 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27781     },
27782     // private
27783     confirmHandler : function(){
27784         if(!this.getSignature()){
27785             return;
27786         }
27787         
27788         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27789         this.setValue(this.getSignature());
27790         this.isConfirmed = true;
27791         
27792         this.fireEvent('confirm', this);
27793     },
27794     // private
27795     // Subclasses should provide the validation implementation by overriding this
27796     validateValue : function(value){
27797         if(this.allowBlank){
27798             return true;
27799         }
27800         
27801         if(this.isConfirmed){
27802             return true;
27803         }
27804         return false;
27805     }
27806 });/*
27807  * Based on:
27808  * Ext JS Library 1.1.1
27809  * Copyright(c) 2006-2007, Ext JS, LLC.
27810  *
27811  * Originally Released Under LGPL - original licence link has changed is not relivant.
27812  *
27813  * Fork - LGPL
27814  * <script type="text/javascript">
27815  */
27816  
27817
27818 /**
27819  * @class Roo.form.ComboBox
27820  * @extends Roo.form.TriggerField
27821  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27822  * @constructor
27823  * Create a new ComboBox.
27824  * @param {Object} config Configuration options
27825  */
27826 Roo.form.Select = function(config){
27827     Roo.form.Select.superclass.constructor.call(this, config);
27828      
27829 };
27830
27831 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27832     /**
27833      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27834      */
27835     /**
27836      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27837      * rendering into an Roo.Editor, defaults to false)
27838      */
27839     /**
27840      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27841      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27842      */
27843     /**
27844      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27845      */
27846     /**
27847      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27848      * the dropdown list (defaults to undefined, with no header element)
27849      */
27850
27851      /**
27852      * @cfg {String/Roo.Template} tpl The template to use to render the output
27853      */
27854      
27855     // private
27856     defaultAutoCreate : {tag: "select"  },
27857     /**
27858      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27859      */
27860     listWidth: undefined,
27861     /**
27862      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27863      * mode = 'remote' or 'text' if mode = 'local')
27864      */
27865     displayField: undefined,
27866     /**
27867      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27868      * mode = 'remote' or 'value' if mode = 'local'). 
27869      * Note: use of a valueField requires the user make a selection
27870      * in order for a value to be mapped.
27871      */
27872     valueField: undefined,
27873     
27874     
27875     /**
27876      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27877      * field's data value (defaults to the underlying DOM element's name)
27878      */
27879     hiddenName: undefined,
27880     /**
27881      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27882      */
27883     listClass: '',
27884     /**
27885      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27886      */
27887     selectedClass: 'x-combo-selected',
27888     /**
27889      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27890      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27891      * which displays a downward arrow icon).
27892      */
27893     triggerClass : 'x-form-arrow-trigger',
27894     /**
27895      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27896      */
27897     shadow:'sides',
27898     /**
27899      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27900      * anchor positions (defaults to 'tl-bl')
27901      */
27902     listAlign: 'tl-bl?',
27903     /**
27904      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27905      */
27906     maxHeight: 300,
27907     /**
27908      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27909      * query specified by the allQuery config option (defaults to 'query')
27910      */
27911     triggerAction: 'query',
27912     /**
27913      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27914      * (defaults to 4, does not apply if editable = false)
27915      */
27916     minChars : 4,
27917     /**
27918      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27919      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27920      */
27921     typeAhead: false,
27922     /**
27923      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27924      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27925      */
27926     queryDelay: 500,
27927     /**
27928      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27929      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27930      */
27931     pageSize: 0,
27932     /**
27933      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27934      * when editable = true (defaults to false)
27935      */
27936     selectOnFocus:false,
27937     /**
27938      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27939      */
27940     queryParam: 'query',
27941     /**
27942      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27943      * when mode = 'remote' (defaults to 'Loading...')
27944      */
27945     loadingText: 'Loading...',
27946     /**
27947      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27948      */
27949     resizable: false,
27950     /**
27951      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27952      */
27953     handleHeight : 8,
27954     /**
27955      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27956      * traditional select (defaults to true)
27957      */
27958     editable: true,
27959     /**
27960      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27961      */
27962     allQuery: '',
27963     /**
27964      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27965      */
27966     mode: 'remote',
27967     /**
27968      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27969      * listWidth has a higher value)
27970      */
27971     minListWidth : 70,
27972     /**
27973      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27974      * allow the user to set arbitrary text into the field (defaults to false)
27975      */
27976     forceSelection:false,
27977     /**
27978      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27979      * if typeAhead = true (defaults to 250)
27980      */
27981     typeAheadDelay : 250,
27982     /**
27983      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27984      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27985      */
27986     valueNotFoundText : undefined,
27987     
27988     /**
27989      * @cfg {String} defaultValue The value displayed after loading the store.
27990      */
27991     defaultValue: '',
27992     
27993     /**
27994      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27995      */
27996     blockFocus : false,
27997     
27998     /**
27999      * @cfg {Boolean} disableClear Disable showing of clear button.
28000      */
28001     disableClear : false,
28002     /**
28003      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28004      */
28005     alwaysQuery : false,
28006     
28007     //private
28008     addicon : false,
28009     editicon: false,
28010     
28011     // element that contains real text value.. (when hidden is used..)
28012      
28013     // private
28014     onRender : function(ct, position){
28015         Roo.form.Field.prototype.onRender.call(this, ct, position);
28016         
28017         if(this.store){
28018             this.store.on('beforeload', this.onBeforeLoad, this);
28019             this.store.on('load', this.onLoad, this);
28020             this.store.on('loadexception', this.onLoadException, this);
28021             this.store.load({});
28022         }
28023         
28024         
28025         
28026     },
28027
28028     // private
28029     initEvents : function(){
28030         //Roo.form.ComboBox.superclass.initEvents.call(this);
28031  
28032     },
28033
28034     onDestroy : function(){
28035        
28036         if(this.store){
28037             this.store.un('beforeload', this.onBeforeLoad, this);
28038             this.store.un('load', this.onLoad, this);
28039             this.store.un('loadexception', this.onLoadException, this);
28040         }
28041         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28042     },
28043
28044     // private
28045     fireKey : function(e){
28046         if(e.isNavKeyPress() && !this.list.isVisible()){
28047             this.fireEvent("specialkey", this, e);
28048         }
28049     },
28050
28051     // private
28052     onResize: function(w, h){
28053         
28054         return; 
28055     
28056         
28057     },
28058
28059     /**
28060      * Allow or prevent the user from directly editing the field text.  If false is passed,
28061      * the user will only be able to select from the items defined in the dropdown list.  This method
28062      * is the runtime equivalent of setting the 'editable' config option at config time.
28063      * @param {Boolean} value True to allow the user to directly edit the field text
28064      */
28065     setEditable : function(value){
28066          
28067     },
28068
28069     // private
28070     onBeforeLoad : function(){
28071         
28072         Roo.log("Select before load");
28073         return;
28074     
28075         this.innerList.update(this.loadingText ?
28076                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28077         //this.restrictHeight();
28078         this.selectedIndex = -1;
28079     },
28080
28081     // private
28082     onLoad : function(){
28083
28084     
28085         var dom = this.el.dom;
28086         dom.innerHTML = '';
28087          var od = dom.ownerDocument;
28088          
28089         if (this.emptyText) {
28090             var op = od.createElement('option');
28091             op.setAttribute('value', '');
28092             op.innerHTML = String.format('{0}', this.emptyText);
28093             dom.appendChild(op);
28094         }
28095         if(this.store.getCount() > 0){
28096            
28097             var vf = this.valueField;
28098             var df = this.displayField;
28099             this.store.data.each(function(r) {
28100                 // which colmsn to use... testing - cdoe / title..
28101                 var op = od.createElement('option');
28102                 op.setAttribute('value', r.data[vf]);
28103                 op.innerHTML = String.format('{0}', r.data[df]);
28104                 dom.appendChild(op);
28105             });
28106             if (typeof(this.defaultValue != 'undefined')) {
28107                 this.setValue(this.defaultValue);
28108             }
28109             
28110              
28111         }else{
28112             //this.onEmptyResults();
28113         }
28114         //this.el.focus();
28115     },
28116     // private
28117     onLoadException : function()
28118     {
28119         dom.innerHTML = '';
28120             
28121         Roo.log("Select on load exception");
28122         return;
28123     
28124         this.collapse();
28125         Roo.log(this.store.reader.jsonData);
28126         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28127             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28128         }
28129         
28130         
28131     },
28132     // private
28133     onTypeAhead : function(){
28134          
28135     },
28136
28137     // private
28138     onSelect : function(record, index){
28139         Roo.log('on select?');
28140         return;
28141         if(this.fireEvent('beforeselect', this, record, index) !== false){
28142             this.setFromData(index > -1 ? record.data : false);
28143             this.collapse();
28144             this.fireEvent('select', this, record, index);
28145         }
28146     },
28147
28148     /**
28149      * Returns the currently selected field value or empty string if no value is set.
28150      * @return {String} value The selected value
28151      */
28152     getValue : function(){
28153         var dom = this.el.dom;
28154         this.value = dom.options[dom.selectedIndex].value;
28155         return this.value;
28156         
28157     },
28158
28159     /**
28160      * Clears any text/value currently set in the field
28161      */
28162     clearValue : function(){
28163         this.value = '';
28164         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28165         
28166     },
28167
28168     /**
28169      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28170      * will be displayed in the field.  If the value does not match the data value of an existing item,
28171      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28172      * Otherwise the field will be blank (although the value will still be set).
28173      * @param {String} value The value to match
28174      */
28175     setValue : function(v){
28176         var d = this.el.dom;
28177         for (var i =0; i < d.options.length;i++) {
28178             if (v == d.options[i].value) {
28179                 d.selectedIndex = i;
28180                 this.value = v;
28181                 return;
28182             }
28183         }
28184         this.clearValue();
28185     },
28186     /**
28187      * @property {Object} the last set data for the element
28188      */
28189     
28190     lastData : false,
28191     /**
28192      * Sets the value of the field based on a object which is related to the record format for the store.
28193      * @param {Object} value the value to set as. or false on reset?
28194      */
28195     setFromData : function(o){
28196         Roo.log('setfrom data?');
28197          
28198         
28199         
28200     },
28201     // private
28202     reset : function(){
28203         this.clearValue();
28204     },
28205     // private
28206     findRecord : function(prop, value){
28207         
28208         return false;
28209     
28210         var record;
28211         if(this.store.getCount() > 0){
28212             this.store.each(function(r){
28213                 if(r.data[prop] == value){
28214                     record = r;
28215                     return false;
28216                 }
28217                 return true;
28218             });
28219         }
28220         return record;
28221     },
28222     
28223     getName: function()
28224     {
28225         // returns hidden if it's set..
28226         if (!this.rendered) {return ''};
28227         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28228         
28229     },
28230      
28231
28232     
28233
28234     // private
28235     onEmptyResults : function(){
28236         Roo.log('empty results');
28237         //this.collapse();
28238     },
28239
28240     /**
28241      * Returns true if the dropdown list is expanded, else false.
28242      */
28243     isExpanded : function(){
28244         return false;
28245     },
28246
28247     /**
28248      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28249      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28250      * @param {String} value The data value of the item to select
28251      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28252      * selected item if it is not currently in view (defaults to true)
28253      * @return {Boolean} True if the value matched an item in the list, else false
28254      */
28255     selectByValue : function(v, scrollIntoView){
28256         Roo.log('select By Value');
28257         return false;
28258     
28259         if(v !== undefined && v !== null){
28260             var r = this.findRecord(this.valueField || this.displayField, v);
28261             if(r){
28262                 this.select(this.store.indexOf(r), scrollIntoView);
28263                 return true;
28264             }
28265         }
28266         return false;
28267     },
28268
28269     /**
28270      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28271      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28272      * @param {Number} index The zero-based index of the list item to select
28273      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28274      * selected item if it is not currently in view (defaults to true)
28275      */
28276     select : function(index, scrollIntoView){
28277         Roo.log('select ');
28278         return  ;
28279         
28280         this.selectedIndex = index;
28281         this.view.select(index);
28282         if(scrollIntoView !== false){
28283             var el = this.view.getNode(index);
28284             if(el){
28285                 this.innerList.scrollChildIntoView(el, false);
28286             }
28287         }
28288     },
28289
28290       
28291
28292     // private
28293     validateBlur : function(){
28294         
28295         return;
28296         
28297     },
28298
28299     // private
28300     initQuery : function(){
28301         this.doQuery(this.getRawValue());
28302     },
28303
28304     // private
28305     doForce : function(){
28306         if(this.el.dom.value.length > 0){
28307             this.el.dom.value =
28308                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28309              
28310         }
28311     },
28312
28313     /**
28314      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28315      * query allowing the query action to be canceled if needed.
28316      * @param {String} query The SQL query to execute
28317      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28318      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28319      * saved in the current store (defaults to false)
28320      */
28321     doQuery : function(q, forceAll){
28322         
28323         Roo.log('doQuery?');
28324         if(q === undefined || q === null){
28325             q = '';
28326         }
28327         var qe = {
28328             query: q,
28329             forceAll: forceAll,
28330             combo: this,
28331             cancel:false
28332         };
28333         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28334             return false;
28335         }
28336         q = qe.query;
28337         forceAll = qe.forceAll;
28338         if(forceAll === true || (q.length >= this.minChars)){
28339             if(this.lastQuery != q || this.alwaysQuery){
28340                 this.lastQuery = q;
28341                 if(this.mode == 'local'){
28342                     this.selectedIndex = -1;
28343                     if(forceAll){
28344                         this.store.clearFilter();
28345                     }else{
28346                         this.store.filter(this.displayField, q);
28347                     }
28348                     this.onLoad();
28349                 }else{
28350                     this.store.baseParams[this.queryParam] = q;
28351                     this.store.load({
28352                         params: this.getParams(q)
28353                     });
28354                     this.expand();
28355                 }
28356             }else{
28357                 this.selectedIndex = -1;
28358                 this.onLoad();   
28359             }
28360         }
28361     },
28362
28363     // private
28364     getParams : function(q){
28365         var p = {};
28366         //p[this.queryParam] = q;
28367         if(this.pageSize){
28368             p.start = 0;
28369             p.limit = this.pageSize;
28370         }
28371         return p;
28372     },
28373
28374     /**
28375      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28376      */
28377     collapse : function(){
28378         
28379     },
28380
28381     // private
28382     collapseIf : function(e){
28383         
28384     },
28385
28386     /**
28387      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28388      */
28389     expand : function(){
28390         
28391     } ,
28392
28393     // private
28394      
28395
28396     /** 
28397     * @cfg {Boolean} grow 
28398     * @hide 
28399     */
28400     /** 
28401     * @cfg {Number} growMin 
28402     * @hide 
28403     */
28404     /** 
28405     * @cfg {Number} growMax 
28406     * @hide 
28407     */
28408     /**
28409      * @hide
28410      * @method autoSize
28411      */
28412     
28413     setWidth : function()
28414     {
28415         
28416     },
28417     getResizeEl : function(){
28418         return this.el;
28419     }
28420 });//<script type="text/javasscript">
28421  
28422
28423 /**
28424  * @class Roo.DDView
28425  * A DnD enabled version of Roo.View.
28426  * @param {Element/String} container The Element in which to create the View.
28427  * @param {String} tpl The template string used to create the markup for each element of the View
28428  * @param {Object} config The configuration properties. These include all the config options of
28429  * {@link Roo.View} plus some specific to this class.<br>
28430  * <p>
28431  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28432  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28433  * <p>
28434  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28435 .x-view-drag-insert-above {
28436         border-top:1px dotted #3366cc;
28437 }
28438 .x-view-drag-insert-below {
28439         border-bottom:1px dotted #3366cc;
28440 }
28441 </code></pre>
28442  * 
28443  */
28444  
28445 Roo.DDView = function(container, tpl, config) {
28446     Roo.DDView.superclass.constructor.apply(this, arguments);
28447     this.getEl().setStyle("outline", "0px none");
28448     this.getEl().unselectable();
28449     if (this.dragGroup) {
28450                 this.setDraggable(this.dragGroup.split(","));
28451     }
28452     if (this.dropGroup) {
28453                 this.setDroppable(this.dropGroup.split(","));
28454     }
28455     if (this.deletable) {
28456         this.setDeletable();
28457     }
28458     this.isDirtyFlag = false;
28459         this.addEvents({
28460                 "drop" : true
28461         });
28462 };
28463
28464 Roo.extend(Roo.DDView, Roo.View, {
28465 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28466 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28467 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28468 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28469
28470         isFormField: true,
28471
28472         reset: Roo.emptyFn,
28473         
28474         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28475
28476         validate: function() {
28477                 return true;
28478         },
28479         
28480         destroy: function() {
28481                 this.purgeListeners();
28482                 this.getEl.removeAllListeners();
28483                 this.getEl().remove();
28484                 if (this.dragZone) {
28485                         if (this.dragZone.destroy) {
28486                                 this.dragZone.destroy();
28487                         }
28488                 }
28489                 if (this.dropZone) {
28490                         if (this.dropZone.destroy) {
28491                                 this.dropZone.destroy();
28492                         }
28493                 }
28494         },
28495
28496 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28497         getName: function() {
28498                 return this.name;
28499         },
28500
28501 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28502         setValue: function(v) {
28503                 if (!this.store) {
28504                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28505                 }
28506                 var data = {};
28507                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28508                 this.store.proxy = new Roo.data.MemoryProxy(data);
28509                 this.store.load();
28510         },
28511
28512 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28513         getValue: function() {
28514                 var result = '(';
28515                 this.store.each(function(rec) {
28516                         result += rec.id + ',';
28517                 });
28518                 return result.substr(0, result.length - 1) + ')';
28519         },
28520         
28521         getIds: function() {
28522                 var i = 0, result = new Array(this.store.getCount());
28523                 this.store.each(function(rec) {
28524                         result[i++] = rec.id;
28525                 });
28526                 return result;
28527         },
28528         
28529         isDirty: function() {
28530                 return this.isDirtyFlag;
28531         },
28532
28533 /**
28534  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28535  *      whole Element becomes the target, and this causes the drop gesture to append.
28536  */
28537     getTargetFromEvent : function(e) {
28538                 var target = e.getTarget();
28539                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28540                 target = target.parentNode;
28541                 }
28542                 if (!target) {
28543                         target = this.el.dom.lastChild || this.el.dom;
28544                 }
28545                 return target;
28546     },
28547
28548 /**
28549  *      Create the drag data which consists of an object which has the property "ddel" as
28550  *      the drag proxy element. 
28551  */
28552     getDragData : function(e) {
28553         var target = this.findItemFromChild(e.getTarget());
28554                 if(target) {
28555                         this.handleSelection(e);
28556                         var selNodes = this.getSelectedNodes();
28557             var dragData = {
28558                 source: this,
28559                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28560                 nodes: selNodes,
28561                 records: []
28562                         };
28563                         var selectedIndices = this.getSelectedIndexes();
28564                         for (var i = 0; i < selectedIndices.length; i++) {
28565                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28566                         }
28567                         if (selNodes.length == 1) {
28568                                 dragData.ddel = target.cloneNode(true); // the div element
28569                         } else {
28570                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28571                                 div.className = 'multi-proxy';
28572                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28573                                         div.appendChild(selNodes[i].cloneNode(true));
28574                                 }
28575                                 dragData.ddel = div;
28576                         }
28577             //console.log(dragData)
28578             //console.log(dragData.ddel.innerHTML)
28579                         return dragData;
28580                 }
28581         //console.log('nodragData')
28582                 return false;
28583     },
28584     
28585 /**     Specify to which ddGroup items in this DDView may be dragged. */
28586     setDraggable: function(ddGroup) {
28587         if (ddGroup instanceof Array) {
28588                 Roo.each(ddGroup, this.setDraggable, this);
28589                 return;
28590         }
28591         if (this.dragZone) {
28592                 this.dragZone.addToGroup(ddGroup);
28593         } else {
28594                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28595                                 containerScroll: true,
28596                                 ddGroup: ddGroup 
28597
28598                         });
28599 //                      Draggability implies selection. DragZone's mousedown selects the element.
28600                         if (!this.multiSelect) { this.singleSelect = true; }
28601
28602 //                      Wire the DragZone's handlers up to methods in *this*
28603                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28604                 }
28605     },
28606
28607 /**     Specify from which ddGroup this DDView accepts drops. */
28608     setDroppable: function(ddGroup) {
28609         if (ddGroup instanceof Array) {
28610                 Roo.each(ddGroup, this.setDroppable, this);
28611                 return;
28612         }
28613         if (this.dropZone) {
28614                 this.dropZone.addToGroup(ddGroup);
28615         } else {
28616                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28617                                 containerScroll: true,
28618                                 ddGroup: ddGroup
28619                         });
28620
28621 //                      Wire the DropZone's handlers up to methods in *this*
28622                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28623                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28624                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28625                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28626                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28627                 }
28628     },
28629
28630 /**     Decide whether to drop above or below a View node. */
28631     getDropPoint : function(e, n, dd){
28632         if (n == this.el.dom) { return "above"; }
28633                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28634                 var c = t + (b - t) / 2;
28635                 var y = Roo.lib.Event.getPageY(e);
28636                 if(y <= c) {
28637                         return "above";
28638                 }else{
28639                         return "below";
28640                 }
28641     },
28642
28643     onNodeEnter : function(n, dd, e, data){
28644                 return false;
28645     },
28646     
28647     onNodeOver : function(n, dd, e, data){
28648                 var pt = this.getDropPoint(e, n, dd);
28649                 // set the insert point style on the target node
28650                 var dragElClass = this.dropNotAllowed;
28651                 if (pt) {
28652                         var targetElClass;
28653                         if (pt == "above"){
28654                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28655                                 targetElClass = "x-view-drag-insert-above";
28656                         } else {
28657                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28658                                 targetElClass = "x-view-drag-insert-below";
28659                         }
28660                         if (this.lastInsertClass != targetElClass){
28661                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28662                                 this.lastInsertClass = targetElClass;
28663                         }
28664                 }
28665                 return dragElClass;
28666         },
28667
28668     onNodeOut : function(n, dd, e, data){
28669                 this.removeDropIndicators(n);
28670     },
28671
28672     onNodeDrop : function(n, dd, e, data){
28673         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28674                 return false;
28675         }
28676         var pt = this.getDropPoint(e, n, dd);
28677                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28678                 if (pt == "below") { insertAt++; }
28679                 for (var i = 0; i < data.records.length; i++) {
28680                         var r = data.records[i];
28681                         var dup = this.store.getById(r.id);
28682                         if (dup && (dd != this.dragZone)) {
28683                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28684                         } else {
28685                                 if (data.copy) {
28686                                         this.store.insert(insertAt++, r.copy());
28687                                 } else {
28688                                         data.source.isDirtyFlag = true;
28689                                         r.store.remove(r);
28690                                         this.store.insert(insertAt++, r);
28691                                 }
28692                                 this.isDirtyFlag = true;
28693                         }
28694                 }
28695                 this.dragZone.cachedTarget = null;
28696                 return true;
28697     },
28698
28699     removeDropIndicators : function(n){
28700                 if(n){
28701                         Roo.fly(n).removeClass([
28702                                 "x-view-drag-insert-above",
28703                                 "x-view-drag-insert-below"]);
28704                         this.lastInsertClass = "_noclass";
28705                 }
28706     },
28707
28708 /**
28709  *      Utility method. Add a delete option to the DDView's context menu.
28710  *      @param {String} imageUrl The URL of the "delete" icon image.
28711  */
28712         setDeletable: function(imageUrl) {
28713                 if (!this.singleSelect && !this.multiSelect) {
28714                         this.singleSelect = true;
28715                 }
28716                 var c = this.getContextMenu();
28717                 this.contextMenu.on("itemclick", function(item) {
28718                         switch (item.id) {
28719                                 case "delete":
28720                                         this.remove(this.getSelectedIndexes());
28721                                         break;
28722                         }
28723                 }, this);
28724                 this.contextMenu.add({
28725                         icon: imageUrl,
28726                         id: "delete",
28727                         text: 'Delete'
28728                 });
28729         },
28730         
28731 /**     Return the context menu for this DDView. */
28732         getContextMenu: function() {
28733                 if (!this.contextMenu) {
28734 //                      Create the View's context menu
28735                         this.contextMenu = new Roo.menu.Menu({
28736                                 id: this.id + "-contextmenu"
28737                         });
28738                         this.el.on("contextmenu", this.showContextMenu, this);
28739                 }
28740                 return this.contextMenu;
28741         },
28742         
28743         disableContextMenu: function() {
28744                 if (this.contextMenu) {
28745                         this.el.un("contextmenu", this.showContextMenu, this);
28746                 }
28747         },
28748
28749         showContextMenu: function(e, item) {
28750         item = this.findItemFromChild(e.getTarget());
28751                 if (item) {
28752                         e.stopEvent();
28753                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28754                         this.contextMenu.showAt(e.getXY());
28755             }
28756     },
28757
28758 /**
28759  *      Remove {@link Roo.data.Record}s at the specified indices.
28760  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28761  */
28762     remove: function(selectedIndices) {
28763                 selectedIndices = [].concat(selectedIndices);
28764                 for (var i = 0; i < selectedIndices.length; i++) {
28765                         var rec = this.store.getAt(selectedIndices[i]);
28766                         this.store.remove(rec);
28767                 }
28768     },
28769
28770 /**
28771  *      Double click fires the event, but also, if this is draggable, and there is only one other
28772  *      related DropZone, it transfers the selected node.
28773  */
28774     onDblClick : function(e){
28775         var item = this.findItemFromChild(e.getTarget());
28776         if(item){
28777             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28778                 return false;
28779             }
28780             if (this.dragGroup) {
28781                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28782                     while (targets.indexOf(this.dropZone) > -1) {
28783                             targets.remove(this.dropZone);
28784                                 }
28785                     if (targets.length == 1) {
28786                                         this.dragZone.cachedTarget = null;
28787                         var el = Roo.get(targets[0].getEl());
28788                         var box = el.getBox(true);
28789                         targets[0].onNodeDrop(el.dom, {
28790                                 target: el.dom,
28791                                 xy: [box.x, box.y + box.height - 1]
28792                         }, null, this.getDragData(e));
28793                     }
28794                 }
28795         }
28796     },
28797     
28798     handleSelection: function(e) {
28799                 this.dragZone.cachedTarget = null;
28800         var item = this.findItemFromChild(e.getTarget());
28801         if (!item) {
28802                 this.clearSelections(true);
28803                 return;
28804         }
28805                 if (item && (this.multiSelect || this.singleSelect)){
28806                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28807                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28808                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28809                                 this.unselect(item);
28810                         } else {
28811                                 this.select(item, this.multiSelect && e.ctrlKey);
28812                                 this.lastSelection = item;
28813                         }
28814                 }
28815     },
28816
28817     onItemClick : function(item, index, e){
28818                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28819                         return false;
28820                 }
28821                 return true;
28822     },
28823
28824     unselect : function(nodeInfo, suppressEvent){
28825                 var node = this.getNode(nodeInfo);
28826                 if(node && this.isSelected(node)){
28827                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28828                                 Roo.fly(node).removeClass(this.selectedClass);
28829                                 this.selections.remove(node);
28830                                 if(!suppressEvent){
28831                                         this.fireEvent("selectionchange", this, this.selections);
28832                                 }
28833                         }
28834                 }
28835     }
28836 });
28837 /*
28838  * Based on:
28839  * Ext JS Library 1.1.1
28840  * Copyright(c) 2006-2007, Ext JS, LLC.
28841  *
28842  * Originally Released Under LGPL - original licence link has changed is not relivant.
28843  *
28844  * Fork - LGPL
28845  * <script type="text/javascript">
28846  */
28847  
28848 /**
28849  * @class Roo.LayoutManager
28850  * @extends Roo.util.Observable
28851  * Base class for layout managers.
28852  */
28853 Roo.LayoutManager = function(container, config){
28854     Roo.LayoutManager.superclass.constructor.call(this);
28855     this.el = Roo.get(container);
28856     // ie scrollbar fix
28857     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28858         document.body.scroll = "no";
28859     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28860         this.el.position('relative');
28861     }
28862     this.id = this.el.id;
28863     this.el.addClass("x-layout-container");
28864     /** false to disable window resize monitoring @type Boolean */
28865     this.monitorWindowResize = true;
28866     this.regions = {};
28867     this.addEvents({
28868         /**
28869          * @event layout
28870          * Fires when a layout is performed. 
28871          * @param {Roo.LayoutManager} this
28872          */
28873         "layout" : true,
28874         /**
28875          * @event regionresized
28876          * Fires when the user resizes a region. 
28877          * @param {Roo.LayoutRegion} region The resized region
28878          * @param {Number} newSize The new size (width for east/west, height for north/south)
28879          */
28880         "regionresized" : true,
28881         /**
28882          * @event regioncollapsed
28883          * Fires when a region is collapsed. 
28884          * @param {Roo.LayoutRegion} region The collapsed region
28885          */
28886         "regioncollapsed" : true,
28887         /**
28888          * @event regionexpanded
28889          * Fires when a region is expanded.  
28890          * @param {Roo.LayoutRegion} region The expanded region
28891          */
28892         "regionexpanded" : true
28893     });
28894     this.updating = false;
28895     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28896 };
28897
28898 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28899     /**
28900      * Returns true if this layout is currently being updated
28901      * @return {Boolean}
28902      */
28903     isUpdating : function(){
28904         return this.updating; 
28905     },
28906     
28907     /**
28908      * Suspend the LayoutManager from doing auto-layouts while
28909      * making multiple add or remove calls
28910      */
28911     beginUpdate : function(){
28912         this.updating = true;    
28913     },
28914     
28915     /**
28916      * Restore auto-layouts and optionally disable the manager from performing a layout
28917      * @param {Boolean} noLayout true to disable a layout update 
28918      */
28919     endUpdate : function(noLayout){
28920         this.updating = false;
28921         if(!noLayout){
28922             this.layout();
28923         }    
28924     },
28925     
28926     layout: function(){
28927         
28928     },
28929     
28930     onRegionResized : function(region, newSize){
28931         this.fireEvent("regionresized", region, newSize);
28932         this.layout();
28933     },
28934     
28935     onRegionCollapsed : function(region){
28936         this.fireEvent("regioncollapsed", region);
28937     },
28938     
28939     onRegionExpanded : function(region){
28940         this.fireEvent("regionexpanded", region);
28941     },
28942         
28943     /**
28944      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28945      * performs box-model adjustments.
28946      * @return {Object} The size as an object {width: (the width), height: (the height)}
28947      */
28948     getViewSize : function(){
28949         var size;
28950         if(this.el.dom != document.body){
28951             size = this.el.getSize();
28952         }else{
28953             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28954         }
28955         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28956         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28957         return size;
28958     },
28959     
28960     /**
28961      * Returns the Element this layout is bound to.
28962      * @return {Roo.Element}
28963      */
28964     getEl : function(){
28965         return this.el;
28966     },
28967     
28968     /**
28969      * Returns the specified region.
28970      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28971      * @return {Roo.LayoutRegion}
28972      */
28973     getRegion : function(target){
28974         return this.regions[target.toLowerCase()];
28975     },
28976     
28977     onWindowResize : function(){
28978         if(this.monitorWindowResize){
28979             this.layout();
28980         }
28981     }
28982 });/*
28983  * Based on:
28984  * Ext JS Library 1.1.1
28985  * Copyright(c) 2006-2007, Ext JS, LLC.
28986  *
28987  * Originally Released Under LGPL - original licence link has changed is not relivant.
28988  *
28989  * Fork - LGPL
28990  * <script type="text/javascript">
28991  */
28992 /**
28993  * @class Roo.BorderLayout
28994  * @extends Roo.LayoutManager
28995  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28996  * please see: <br><br>
28997  * <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>
28998  * <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>
28999  * Example:
29000  <pre><code>
29001  var layout = new Roo.BorderLayout(document.body, {
29002     north: {
29003         initialSize: 25,
29004         titlebar: false
29005     },
29006     west: {
29007         split:true,
29008         initialSize: 200,
29009         minSize: 175,
29010         maxSize: 400,
29011         titlebar: true,
29012         collapsible: true
29013     },
29014     east: {
29015         split:true,
29016         initialSize: 202,
29017         minSize: 175,
29018         maxSize: 400,
29019         titlebar: true,
29020         collapsible: true
29021     },
29022     south: {
29023         split:true,
29024         initialSize: 100,
29025         minSize: 100,
29026         maxSize: 200,
29027         titlebar: true,
29028         collapsible: true
29029     },
29030     center: {
29031         titlebar: true,
29032         autoScroll:true,
29033         resizeTabs: true,
29034         minTabWidth: 50,
29035         preferredTabWidth: 150
29036     }
29037 });
29038
29039 // shorthand
29040 var CP = Roo.ContentPanel;
29041
29042 layout.beginUpdate();
29043 layout.add("north", new CP("north", "North"));
29044 layout.add("south", new CP("south", {title: "South", closable: true}));
29045 layout.add("west", new CP("west", {title: "West"}));
29046 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29047 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29048 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29049 layout.getRegion("center").showPanel("center1");
29050 layout.endUpdate();
29051 </code></pre>
29052
29053 <b>The container the layout is rendered into can be either the body element or any other element.
29054 If it is not the body element, the container needs to either be an absolute positioned element,
29055 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29056 the container size if it is not the body element.</b>
29057
29058 * @constructor
29059 * Create a new BorderLayout
29060 * @param {String/HTMLElement/Element} container The container this layout is bound to
29061 * @param {Object} config Configuration options
29062  */
29063 Roo.BorderLayout = function(container, config){
29064     config = config || {};
29065     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29066     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29067     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29068         var target = this.factory.validRegions[i];
29069         if(config[target]){
29070             this.addRegion(target, config[target]);
29071         }
29072     }
29073 };
29074
29075 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29076     /**
29077      * Creates and adds a new region if it doesn't already exist.
29078      * @param {String} target The target region key (north, south, east, west or center).
29079      * @param {Object} config The regions config object
29080      * @return {BorderLayoutRegion} The new region
29081      */
29082     addRegion : function(target, config){
29083         if(!this.regions[target]){
29084             var r = this.factory.create(target, this, config);
29085             this.bindRegion(target, r);
29086         }
29087         return this.regions[target];
29088     },
29089
29090     // private (kinda)
29091     bindRegion : function(name, r){
29092         this.regions[name] = r;
29093         r.on("visibilitychange", this.layout, this);
29094         r.on("paneladded", this.layout, this);
29095         r.on("panelremoved", this.layout, this);
29096         r.on("invalidated", this.layout, this);
29097         r.on("resized", this.onRegionResized, this);
29098         r.on("collapsed", this.onRegionCollapsed, this);
29099         r.on("expanded", this.onRegionExpanded, this);
29100     },
29101
29102     /**
29103      * Performs a layout update.
29104      */
29105     layout : function(){
29106         if(this.updating) {
29107             return;
29108         }
29109         var size = this.getViewSize();
29110         var w = size.width;
29111         var h = size.height;
29112         var centerW = w;
29113         var centerH = h;
29114         var centerY = 0;
29115         var centerX = 0;
29116         //var x = 0, y = 0;
29117
29118         var rs = this.regions;
29119         var north = rs["north"];
29120         var south = rs["south"]; 
29121         var west = rs["west"];
29122         var east = rs["east"];
29123         var center = rs["center"];
29124         //if(this.hideOnLayout){ // not supported anymore
29125             //c.el.setStyle("display", "none");
29126         //}
29127         if(north && north.isVisible()){
29128             var b = north.getBox();
29129             var m = north.getMargins();
29130             b.width = w - (m.left+m.right);
29131             b.x = m.left;
29132             b.y = m.top;
29133             centerY = b.height + b.y + m.bottom;
29134             centerH -= centerY;
29135             north.updateBox(this.safeBox(b));
29136         }
29137         if(south && south.isVisible()){
29138             var b = south.getBox();
29139             var m = south.getMargins();
29140             b.width = w - (m.left+m.right);
29141             b.x = m.left;
29142             var totalHeight = (b.height + m.top + m.bottom);
29143             b.y = h - totalHeight + m.top;
29144             centerH -= totalHeight;
29145             south.updateBox(this.safeBox(b));
29146         }
29147         if(west && west.isVisible()){
29148             var b = west.getBox();
29149             var m = west.getMargins();
29150             b.height = centerH - (m.top+m.bottom);
29151             b.x = m.left;
29152             b.y = centerY + m.top;
29153             var totalWidth = (b.width + m.left + m.right);
29154             centerX += totalWidth;
29155             centerW -= totalWidth;
29156             west.updateBox(this.safeBox(b));
29157         }
29158         if(east && east.isVisible()){
29159             var b = east.getBox();
29160             var m = east.getMargins();
29161             b.height = centerH - (m.top+m.bottom);
29162             var totalWidth = (b.width + m.left + m.right);
29163             b.x = w - totalWidth + m.left;
29164             b.y = centerY + m.top;
29165             centerW -= totalWidth;
29166             east.updateBox(this.safeBox(b));
29167         }
29168         if(center){
29169             var m = center.getMargins();
29170             var centerBox = {
29171                 x: centerX + m.left,
29172                 y: centerY + m.top,
29173                 width: centerW - (m.left+m.right),
29174                 height: centerH - (m.top+m.bottom)
29175             };
29176             //if(this.hideOnLayout){
29177                 //center.el.setStyle("display", "block");
29178             //}
29179             center.updateBox(this.safeBox(centerBox));
29180         }
29181         this.el.repaint();
29182         this.fireEvent("layout", this);
29183     },
29184
29185     // private
29186     safeBox : function(box){
29187         box.width = Math.max(0, box.width);
29188         box.height = Math.max(0, box.height);
29189         return box;
29190     },
29191
29192     /**
29193      * Adds a ContentPanel (or subclass) to this layout.
29194      * @param {String} target The target region key (north, south, east, west or center).
29195      * @param {Roo.ContentPanel} panel The panel to add
29196      * @return {Roo.ContentPanel} The added panel
29197      */
29198     add : function(target, panel){
29199          
29200         target = target.toLowerCase();
29201         return this.regions[target].add(panel);
29202     },
29203
29204     /**
29205      * Remove a ContentPanel (or subclass) to this layout.
29206      * @param {String} target The target region key (north, south, east, west or center).
29207      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29208      * @return {Roo.ContentPanel} The removed panel
29209      */
29210     remove : function(target, panel){
29211         target = target.toLowerCase();
29212         return this.regions[target].remove(panel);
29213     },
29214
29215     /**
29216      * Searches all regions for a panel with the specified id
29217      * @param {String} panelId
29218      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29219      */
29220     findPanel : function(panelId){
29221         var rs = this.regions;
29222         for(var target in rs){
29223             if(typeof rs[target] != "function"){
29224                 var p = rs[target].getPanel(panelId);
29225                 if(p){
29226                     return p;
29227                 }
29228             }
29229         }
29230         return null;
29231     },
29232
29233     /**
29234      * Searches all regions for a panel with the specified id and activates (shows) it.
29235      * @param {String/ContentPanel} panelId The panels id or the panel itself
29236      * @return {Roo.ContentPanel} The shown panel or null
29237      */
29238     showPanel : function(panelId) {
29239       var rs = this.regions;
29240       for(var target in rs){
29241          var r = rs[target];
29242          if(typeof r != "function"){
29243             if(r.hasPanel(panelId)){
29244                return r.showPanel(panelId);
29245             }
29246          }
29247       }
29248       return null;
29249    },
29250
29251    /**
29252      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29253      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29254      */
29255     restoreState : function(provider){
29256         if(!provider){
29257             provider = Roo.state.Manager;
29258         }
29259         var sm = new Roo.LayoutStateManager();
29260         sm.init(this, provider);
29261     },
29262
29263     /**
29264      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29265      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29266      * a valid ContentPanel config object.  Example:
29267      * <pre><code>
29268 // Create the main layout
29269 var layout = new Roo.BorderLayout('main-ct', {
29270     west: {
29271         split:true,
29272         minSize: 175,
29273         titlebar: true
29274     },
29275     center: {
29276         title:'Components'
29277     }
29278 }, 'main-ct');
29279
29280 // Create and add multiple ContentPanels at once via configs
29281 layout.batchAdd({
29282    west: {
29283        id: 'source-files',
29284        autoCreate:true,
29285        title:'Ext Source Files',
29286        autoScroll:true,
29287        fitToFrame:true
29288    },
29289    center : {
29290        el: cview,
29291        autoScroll:true,
29292        fitToFrame:true,
29293        toolbar: tb,
29294        resizeEl:'cbody'
29295    }
29296 });
29297 </code></pre>
29298      * @param {Object} regions An object containing ContentPanel configs by region name
29299      */
29300     batchAdd : function(regions){
29301         this.beginUpdate();
29302         for(var rname in regions){
29303             var lr = this.regions[rname];
29304             if(lr){
29305                 this.addTypedPanels(lr, regions[rname]);
29306             }
29307         }
29308         this.endUpdate();
29309     },
29310
29311     // private
29312     addTypedPanels : function(lr, ps){
29313         if(typeof ps == 'string'){
29314             lr.add(new Roo.ContentPanel(ps));
29315         }
29316         else if(ps instanceof Array){
29317             for(var i =0, len = ps.length; i < len; i++){
29318                 this.addTypedPanels(lr, ps[i]);
29319             }
29320         }
29321         else if(!ps.events){ // raw config?
29322             var el = ps.el;
29323             delete ps.el; // prevent conflict
29324             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29325         }
29326         else {  // panel object assumed!
29327             lr.add(ps);
29328         }
29329     },
29330     /**
29331      * Adds a xtype elements to the layout.
29332      * <pre><code>
29333
29334 layout.addxtype({
29335        xtype : 'ContentPanel',
29336        region: 'west',
29337        items: [ .... ]
29338    }
29339 );
29340
29341 layout.addxtype({
29342         xtype : 'NestedLayoutPanel',
29343         region: 'west',
29344         layout: {
29345            center: { },
29346            west: { }   
29347         },
29348         items : [ ... list of content panels or nested layout panels.. ]
29349    }
29350 );
29351 </code></pre>
29352      * @param {Object} cfg Xtype definition of item to add.
29353      */
29354     addxtype : function(cfg)
29355     {
29356         // basically accepts a pannel...
29357         // can accept a layout region..!?!?
29358         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29359         
29360         if (!cfg.xtype.match(/Panel$/)) {
29361             return false;
29362         }
29363         var ret = false;
29364         
29365         if (typeof(cfg.region) == 'undefined') {
29366             Roo.log("Failed to add Panel, region was not set");
29367             Roo.log(cfg);
29368             return false;
29369         }
29370         var region = cfg.region;
29371         delete cfg.region;
29372         
29373           
29374         var xitems = [];
29375         if (cfg.items) {
29376             xitems = cfg.items;
29377             delete cfg.items;
29378         }
29379         var nb = false;
29380         
29381         switch(cfg.xtype) 
29382         {
29383             case 'ContentPanel':  // ContentPanel (el, cfg)
29384             case 'ScrollPanel':  // ContentPanel (el, cfg)
29385             case 'ViewPanel': 
29386                 if(cfg.autoCreate) {
29387                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29388                 } else {
29389                     var el = this.el.createChild();
29390                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29391                 }
29392                 
29393                 this.add(region, ret);
29394                 break;
29395             
29396             
29397             case 'TreePanel': // our new panel!
29398                 cfg.el = this.el.createChild();
29399                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29400                 this.add(region, ret);
29401                 break;
29402             
29403             case 'NestedLayoutPanel': 
29404                 // create a new Layout (which is  a Border Layout...
29405                 var el = this.el.createChild();
29406                 var clayout = cfg.layout;
29407                 delete cfg.layout;
29408                 clayout.items   = clayout.items  || [];
29409                 // replace this exitems with the clayout ones..
29410                 xitems = clayout.items;
29411                  
29412                 
29413                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29414                     cfg.background = false;
29415                 }
29416                 var layout = new Roo.BorderLayout(el, clayout);
29417                 
29418                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29419                 //console.log('adding nested layout panel '  + cfg.toSource());
29420                 this.add(region, ret);
29421                 nb = {}; /// find first...
29422                 break;
29423                 
29424             case 'GridPanel': 
29425             
29426                 // needs grid and region
29427                 
29428                 //var el = this.getRegion(region).el.createChild();
29429                 var el = this.el.createChild();
29430                 // create the grid first...
29431                 
29432                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29433                 delete cfg.grid;
29434                 if (region == 'center' && this.active ) {
29435                     cfg.background = false;
29436                 }
29437                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29438                 
29439                 this.add(region, ret);
29440                 if (cfg.background) {
29441                     ret.on('activate', function(gp) {
29442                         if (!gp.grid.rendered) {
29443                             gp.grid.render();
29444                         }
29445                     });
29446                 } else {
29447                     grid.render();
29448                 }
29449                 break;
29450            
29451            
29452            
29453                 
29454                 
29455                 
29456             default:
29457                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29458                     
29459                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29460                     this.add(region, ret);
29461                 } else {
29462                 
29463                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29464                     return null;
29465                 }
29466                 
29467              // GridPanel (grid, cfg)
29468             
29469         }
29470         this.beginUpdate();
29471         // add children..
29472         var region = '';
29473         var abn = {};
29474         Roo.each(xitems, function(i)  {
29475             region = nb && i.region ? i.region : false;
29476             
29477             var add = ret.addxtype(i);
29478            
29479             if (region) {
29480                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29481                 if (!i.background) {
29482                     abn[region] = nb[region] ;
29483                 }
29484             }
29485             
29486         });
29487         this.endUpdate();
29488
29489         // make the last non-background panel active..
29490         //if (nb) { Roo.log(abn); }
29491         if (nb) {
29492             
29493             for(var r in abn) {
29494                 region = this.getRegion(r);
29495                 if (region) {
29496                     // tried using nb[r], but it does not work..
29497                      
29498                     region.showPanel(abn[r]);
29499                    
29500                 }
29501             }
29502         }
29503         return ret;
29504         
29505     }
29506 });
29507
29508 /**
29509  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29510  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29511  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29512  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29513  * <pre><code>
29514 // shorthand
29515 var CP = Roo.ContentPanel;
29516
29517 var layout = Roo.BorderLayout.create({
29518     north: {
29519         initialSize: 25,
29520         titlebar: false,
29521         panels: [new CP("north", "North")]
29522     },
29523     west: {
29524         split:true,
29525         initialSize: 200,
29526         minSize: 175,
29527         maxSize: 400,
29528         titlebar: true,
29529         collapsible: true,
29530         panels: [new CP("west", {title: "West"})]
29531     },
29532     east: {
29533         split:true,
29534         initialSize: 202,
29535         minSize: 175,
29536         maxSize: 400,
29537         titlebar: true,
29538         collapsible: true,
29539         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29540     },
29541     south: {
29542         split:true,
29543         initialSize: 100,
29544         minSize: 100,
29545         maxSize: 200,
29546         titlebar: true,
29547         collapsible: true,
29548         panels: [new CP("south", {title: "South", closable: true})]
29549     },
29550     center: {
29551         titlebar: true,
29552         autoScroll:true,
29553         resizeTabs: true,
29554         minTabWidth: 50,
29555         preferredTabWidth: 150,
29556         panels: [
29557             new CP("center1", {title: "Close Me", closable: true}),
29558             new CP("center2", {title: "Center Panel", closable: false})
29559         ]
29560     }
29561 }, document.body);
29562
29563 layout.getRegion("center").showPanel("center1");
29564 </code></pre>
29565  * @param config
29566  * @param targetEl
29567  */
29568 Roo.BorderLayout.create = function(config, targetEl){
29569     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29570     layout.beginUpdate();
29571     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29572     for(var j = 0, jlen = regions.length; j < jlen; j++){
29573         var lr = regions[j];
29574         if(layout.regions[lr] && config[lr].panels){
29575             var r = layout.regions[lr];
29576             var ps = config[lr].panels;
29577             layout.addTypedPanels(r, ps);
29578         }
29579     }
29580     layout.endUpdate();
29581     return layout;
29582 };
29583
29584 // private
29585 Roo.BorderLayout.RegionFactory = {
29586     // private
29587     validRegions : ["north","south","east","west","center"],
29588
29589     // private
29590     create : function(target, mgr, config){
29591         target = target.toLowerCase();
29592         if(config.lightweight || config.basic){
29593             return new Roo.BasicLayoutRegion(mgr, config, target);
29594         }
29595         switch(target){
29596             case "north":
29597                 return new Roo.NorthLayoutRegion(mgr, config);
29598             case "south":
29599                 return new Roo.SouthLayoutRegion(mgr, config);
29600             case "east":
29601                 return new Roo.EastLayoutRegion(mgr, config);
29602             case "west":
29603                 return new Roo.WestLayoutRegion(mgr, config);
29604             case "center":
29605                 return new Roo.CenterLayoutRegion(mgr, config);
29606         }
29607         throw 'Layout region "'+target+'" not supported.';
29608     }
29609 };/*
29610  * Based on:
29611  * Ext JS Library 1.1.1
29612  * Copyright(c) 2006-2007, Ext JS, LLC.
29613  *
29614  * Originally Released Under LGPL - original licence link has changed is not relivant.
29615  *
29616  * Fork - LGPL
29617  * <script type="text/javascript">
29618  */
29619  
29620 /**
29621  * @class Roo.BasicLayoutRegion
29622  * @extends Roo.util.Observable
29623  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29624  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29625  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29626  */
29627 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29628     this.mgr = mgr;
29629     this.position  = pos;
29630     this.events = {
29631         /**
29632          * @scope Roo.BasicLayoutRegion
29633          */
29634         
29635         /**
29636          * @event beforeremove
29637          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29638          * @param {Roo.LayoutRegion} this
29639          * @param {Roo.ContentPanel} panel The panel
29640          * @param {Object} e The cancel event object
29641          */
29642         "beforeremove" : true,
29643         /**
29644          * @event invalidated
29645          * Fires when the layout for this region is changed.
29646          * @param {Roo.LayoutRegion} this
29647          */
29648         "invalidated" : true,
29649         /**
29650          * @event visibilitychange
29651          * Fires when this region is shown or hidden 
29652          * @param {Roo.LayoutRegion} this
29653          * @param {Boolean} visibility true or false
29654          */
29655         "visibilitychange" : true,
29656         /**
29657          * @event paneladded
29658          * Fires when a panel is added. 
29659          * @param {Roo.LayoutRegion} this
29660          * @param {Roo.ContentPanel} panel The panel
29661          */
29662         "paneladded" : true,
29663         /**
29664          * @event panelremoved
29665          * Fires when a panel is removed. 
29666          * @param {Roo.LayoutRegion} this
29667          * @param {Roo.ContentPanel} panel The panel
29668          */
29669         "panelremoved" : true,
29670         /**
29671          * @event beforecollapse
29672          * Fires when this region before collapse.
29673          * @param {Roo.LayoutRegion} this
29674          */
29675         "beforecollapse" : true,
29676         /**
29677          * @event collapsed
29678          * Fires when this region is collapsed.
29679          * @param {Roo.LayoutRegion} this
29680          */
29681         "collapsed" : true,
29682         /**
29683          * @event expanded
29684          * Fires when this region is expanded.
29685          * @param {Roo.LayoutRegion} this
29686          */
29687         "expanded" : true,
29688         /**
29689          * @event slideshow
29690          * Fires when this region is slid into view.
29691          * @param {Roo.LayoutRegion} this
29692          */
29693         "slideshow" : true,
29694         /**
29695          * @event slidehide
29696          * Fires when this region slides out of view. 
29697          * @param {Roo.LayoutRegion} this
29698          */
29699         "slidehide" : true,
29700         /**
29701          * @event panelactivated
29702          * Fires when a panel is activated. 
29703          * @param {Roo.LayoutRegion} this
29704          * @param {Roo.ContentPanel} panel The activated panel
29705          */
29706         "panelactivated" : true,
29707         /**
29708          * @event resized
29709          * Fires when the user resizes this region. 
29710          * @param {Roo.LayoutRegion} this
29711          * @param {Number} newSize The new size (width for east/west, height for north/south)
29712          */
29713         "resized" : true
29714     };
29715     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29716     this.panels = new Roo.util.MixedCollection();
29717     this.panels.getKey = this.getPanelId.createDelegate(this);
29718     this.box = null;
29719     this.activePanel = null;
29720     // ensure listeners are added...
29721     
29722     if (config.listeners || config.events) {
29723         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29724             listeners : config.listeners || {},
29725             events : config.events || {}
29726         });
29727     }
29728     
29729     if(skipConfig !== true){
29730         this.applyConfig(config);
29731     }
29732 };
29733
29734 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29735     getPanelId : function(p){
29736         return p.getId();
29737     },
29738     
29739     applyConfig : function(config){
29740         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29741         this.config = config;
29742         
29743     },
29744     
29745     /**
29746      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29747      * the width, for horizontal (north, south) the height.
29748      * @param {Number} newSize The new width or height
29749      */
29750     resizeTo : function(newSize){
29751         var el = this.el ? this.el :
29752                  (this.activePanel ? this.activePanel.getEl() : null);
29753         if(el){
29754             switch(this.position){
29755                 case "east":
29756                 case "west":
29757                     el.setWidth(newSize);
29758                     this.fireEvent("resized", this, newSize);
29759                 break;
29760                 case "north":
29761                 case "south":
29762                     el.setHeight(newSize);
29763                     this.fireEvent("resized", this, newSize);
29764                 break;                
29765             }
29766         }
29767     },
29768     
29769     getBox : function(){
29770         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29771     },
29772     
29773     getMargins : function(){
29774         return this.margins;
29775     },
29776     
29777     updateBox : function(box){
29778         this.box = box;
29779         var el = this.activePanel.getEl();
29780         el.dom.style.left = box.x + "px";
29781         el.dom.style.top = box.y + "px";
29782         this.activePanel.setSize(box.width, box.height);
29783     },
29784     
29785     /**
29786      * Returns the container element for this region.
29787      * @return {Roo.Element}
29788      */
29789     getEl : function(){
29790         return this.activePanel;
29791     },
29792     
29793     /**
29794      * Returns true if this region is currently visible.
29795      * @return {Boolean}
29796      */
29797     isVisible : function(){
29798         return this.activePanel ? true : false;
29799     },
29800     
29801     setActivePanel : function(panel){
29802         panel = this.getPanel(panel);
29803         if(this.activePanel && this.activePanel != panel){
29804             this.activePanel.setActiveState(false);
29805             this.activePanel.getEl().setLeftTop(-10000,-10000);
29806         }
29807         this.activePanel = panel;
29808         panel.setActiveState(true);
29809         if(this.box){
29810             panel.setSize(this.box.width, this.box.height);
29811         }
29812         this.fireEvent("panelactivated", this, panel);
29813         this.fireEvent("invalidated");
29814     },
29815     
29816     /**
29817      * Show the specified panel.
29818      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29819      * @return {Roo.ContentPanel} The shown panel or null
29820      */
29821     showPanel : function(panel){
29822         if(panel = this.getPanel(panel)){
29823             this.setActivePanel(panel);
29824         }
29825         return panel;
29826     },
29827     
29828     /**
29829      * Get the active panel for this region.
29830      * @return {Roo.ContentPanel} The active panel or null
29831      */
29832     getActivePanel : function(){
29833         return this.activePanel;
29834     },
29835     
29836     /**
29837      * Add the passed ContentPanel(s)
29838      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29839      * @return {Roo.ContentPanel} The panel added (if only one was added)
29840      */
29841     add : function(panel){
29842         if(arguments.length > 1){
29843             for(var i = 0, len = arguments.length; i < len; i++) {
29844                 this.add(arguments[i]);
29845             }
29846             return null;
29847         }
29848         if(this.hasPanel(panel)){
29849             this.showPanel(panel);
29850             return panel;
29851         }
29852         var el = panel.getEl();
29853         if(el.dom.parentNode != this.mgr.el.dom){
29854             this.mgr.el.dom.appendChild(el.dom);
29855         }
29856         if(panel.setRegion){
29857             panel.setRegion(this);
29858         }
29859         this.panels.add(panel);
29860         el.setStyle("position", "absolute");
29861         if(!panel.background){
29862             this.setActivePanel(panel);
29863             if(this.config.initialSize && this.panels.getCount()==1){
29864                 this.resizeTo(this.config.initialSize);
29865             }
29866         }
29867         this.fireEvent("paneladded", this, panel);
29868         return panel;
29869     },
29870     
29871     /**
29872      * Returns true if the panel is in this region.
29873      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29874      * @return {Boolean}
29875      */
29876     hasPanel : function(panel){
29877         if(typeof panel == "object"){ // must be panel obj
29878             panel = panel.getId();
29879         }
29880         return this.getPanel(panel) ? true : false;
29881     },
29882     
29883     /**
29884      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29885      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29886      * @param {Boolean} preservePanel Overrides the config preservePanel option
29887      * @return {Roo.ContentPanel} The panel that was removed
29888      */
29889     remove : function(panel, preservePanel){
29890         panel = this.getPanel(panel);
29891         if(!panel){
29892             return null;
29893         }
29894         var e = {};
29895         this.fireEvent("beforeremove", this, panel, e);
29896         if(e.cancel === true){
29897             return null;
29898         }
29899         var panelId = panel.getId();
29900         this.panels.removeKey(panelId);
29901         return panel;
29902     },
29903     
29904     /**
29905      * Returns the panel specified or null if it's not in this region.
29906      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29907      * @return {Roo.ContentPanel}
29908      */
29909     getPanel : function(id){
29910         if(typeof id == "object"){ // must be panel obj
29911             return id;
29912         }
29913         return this.panels.get(id);
29914     },
29915     
29916     /**
29917      * Returns this regions position (north/south/east/west/center).
29918      * @return {String} 
29919      */
29920     getPosition: function(){
29921         return this.position;    
29922     }
29923 });/*
29924  * Based on:
29925  * Ext JS Library 1.1.1
29926  * Copyright(c) 2006-2007, Ext JS, LLC.
29927  *
29928  * Originally Released Under LGPL - original licence link has changed is not relivant.
29929  *
29930  * Fork - LGPL
29931  * <script type="text/javascript">
29932  */
29933  
29934 /**
29935  * @class Roo.LayoutRegion
29936  * @extends Roo.BasicLayoutRegion
29937  * This class represents a region in a layout manager.
29938  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29939  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29940  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29941  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29942  * @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})
29943  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29944  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29945  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29946  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29947  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29948  * @cfg {String}    title           The title for the region (overrides panel titles)
29949  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29950  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29951  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29952  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29953  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29954  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29955  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29956  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29957  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29958  * @cfg {Boolean}   showPin         True to show a pin button
29959  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29960  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29961  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29962  * @cfg {Number}    width           For East/West panels
29963  * @cfg {Number}    height          For North/South panels
29964  * @cfg {Boolean}   split           To show the splitter
29965  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29966  */
29967 Roo.LayoutRegion = function(mgr, config, pos){
29968     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29969     var dh = Roo.DomHelper;
29970     /** This region's container element 
29971     * @type Roo.Element */
29972     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29973     /** This region's title element 
29974     * @type Roo.Element */
29975
29976     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29977         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29978         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29979     ]}, true);
29980     this.titleEl.enableDisplayMode();
29981     /** This region's title text element 
29982     * @type HTMLElement */
29983     this.titleTextEl = this.titleEl.dom.firstChild;
29984     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29985     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29986     this.closeBtn.enableDisplayMode();
29987     this.closeBtn.on("click", this.closeClicked, this);
29988     this.closeBtn.hide();
29989
29990     this.createBody(config);
29991     this.visible = true;
29992     this.collapsed = false;
29993
29994     if(config.hideWhenEmpty){
29995         this.hide();
29996         this.on("paneladded", this.validateVisibility, this);
29997         this.on("panelremoved", this.validateVisibility, this);
29998     }
29999     this.applyConfig(config);
30000 };
30001
30002 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30003
30004     createBody : function(){
30005         /** This region's body element 
30006         * @type Roo.Element */
30007         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30008     },
30009
30010     applyConfig : function(c){
30011         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30012             var dh = Roo.DomHelper;
30013             if(c.titlebar !== false){
30014                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30015                 this.collapseBtn.on("click", this.collapse, this);
30016                 this.collapseBtn.enableDisplayMode();
30017
30018                 if(c.showPin === true || this.showPin){
30019                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30020                     this.stickBtn.enableDisplayMode();
30021                     this.stickBtn.on("click", this.expand, this);
30022                     this.stickBtn.hide();
30023                 }
30024             }
30025             /** This region's collapsed element
30026             * @type Roo.Element */
30027             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30028                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30029             ]}, true);
30030             if(c.floatable !== false){
30031                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30032                this.collapsedEl.on("click", this.collapseClick, this);
30033             }
30034
30035             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30036                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30037                    id: "message", unselectable: "on", style:{"float":"left"}});
30038                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30039              }
30040             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30041             this.expandBtn.on("click", this.expand, this);
30042         }
30043         if(this.collapseBtn){
30044             this.collapseBtn.setVisible(c.collapsible == true);
30045         }
30046         this.cmargins = c.cmargins || this.cmargins ||
30047                          (this.position == "west" || this.position == "east" ?
30048                              {top: 0, left: 2, right:2, bottom: 0} :
30049                              {top: 2, left: 0, right:0, bottom: 2});
30050         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30051         this.bottomTabs = c.tabPosition != "top";
30052         this.autoScroll = c.autoScroll || false;
30053         if(this.autoScroll){
30054             this.bodyEl.setStyle("overflow", "auto");
30055         }else{
30056             this.bodyEl.setStyle("overflow", "hidden");
30057         }
30058         //if(c.titlebar !== false){
30059             if((!c.titlebar && !c.title) || c.titlebar === false){
30060                 this.titleEl.hide();
30061             }else{
30062                 this.titleEl.show();
30063                 if(c.title){
30064                     this.titleTextEl.innerHTML = c.title;
30065                 }
30066             }
30067         //}
30068         this.duration = c.duration || .30;
30069         this.slideDuration = c.slideDuration || .45;
30070         this.config = c;
30071         if(c.collapsed){
30072             this.collapse(true);
30073         }
30074         if(c.hidden){
30075             this.hide();
30076         }
30077     },
30078     /**
30079      * Returns true if this region is currently visible.
30080      * @return {Boolean}
30081      */
30082     isVisible : function(){
30083         return this.visible;
30084     },
30085
30086     /**
30087      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30088      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30089      */
30090     setCollapsedTitle : function(title){
30091         title = title || "&#160;";
30092         if(this.collapsedTitleTextEl){
30093             this.collapsedTitleTextEl.innerHTML = title;
30094         }
30095     },
30096
30097     getBox : function(){
30098         var b;
30099         if(!this.collapsed){
30100             b = this.el.getBox(false, true);
30101         }else{
30102             b = this.collapsedEl.getBox(false, true);
30103         }
30104         return b;
30105     },
30106
30107     getMargins : function(){
30108         return this.collapsed ? this.cmargins : this.margins;
30109     },
30110
30111     highlight : function(){
30112         this.el.addClass("x-layout-panel-dragover");
30113     },
30114
30115     unhighlight : function(){
30116         this.el.removeClass("x-layout-panel-dragover");
30117     },
30118
30119     updateBox : function(box){
30120         this.box = box;
30121         if(!this.collapsed){
30122             this.el.dom.style.left = box.x + "px";
30123             this.el.dom.style.top = box.y + "px";
30124             this.updateBody(box.width, box.height);
30125         }else{
30126             this.collapsedEl.dom.style.left = box.x + "px";
30127             this.collapsedEl.dom.style.top = box.y + "px";
30128             this.collapsedEl.setSize(box.width, box.height);
30129         }
30130         if(this.tabs){
30131             this.tabs.autoSizeTabs();
30132         }
30133     },
30134
30135     updateBody : function(w, h){
30136         if(w !== null){
30137             this.el.setWidth(w);
30138             w -= this.el.getBorderWidth("rl");
30139             if(this.config.adjustments){
30140                 w += this.config.adjustments[0];
30141             }
30142         }
30143         if(h !== null){
30144             this.el.setHeight(h);
30145             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30146             h -= this.el.getBorderWidth("tb");
30147             if(this.config.adjustments){
30148                 h += this.config.adjustments[1];
30149             }
30150             this.bodyEl.setHeight(h);
30151             if(this.tabs){
30152                 h = this.tabs.syncHeight(h);
30153             }
30154         }
30155         if(this.panelSize){
30156             w = w !== null ? w : this.panelSize.width;
30157             h = h !== null ? h : this.panelSize.height;
30158         }
30159         if(this.activePanel){
30160             var el = this.activePanel.getEl();
30161             w = w !== null ? w : el.getWidth();
30162             h = h !== null ? h : el.getHeight();
30163             this.panelSize = {width: w, height: h};
30164             this.activePanel.setSize(w, h);
30165         }
30166         if(Roo.isIE && this.tabs){
30167             this.tabs.el.repaint();
30168         }
30169     },
30170
30171     /**
30172      * Returns the container element for this region.
30173      * @return {Roo.Element}
30174      */
30175     getEl : function(){
30176         return this.el;
30177     },
30178
30179     /**
30180      * Hides this region.
30181      */
30182     hide : function(){
30183         if(!this.collapsed){
30184             this.el.dom.style.left = "-2000px";
30185             this.el.hide();
30186         }else{
30187             this.collapsedEl.dom.style.left = "-2000px";
30188             this.collapsedEl.hide();
30189         }
30190         this.visible = false;
30191         this.fireEvent("visibilitychange", this, false);
30192     },
30193
30194     /**
30195      * Shows this region if it was previously hidden.
30196      */
30197     show : function(){
30198         if(!this.collapsed){
30199             this.el.show();
30200         }else{
30201             this.collapsedEl.show();
30202         }
30203         this.visible = true;
30204         this.fireEvent("visibilitychange", this, true);
30205     },
30206
30207     closeClicked : function(){
30208         if(this.activePanel){
30209             this.remove(this.activePanel);
30210         }
30211     },
30212
30213     collapseClick : function(e){
30214         if(this.isSlid){
30215            e.stopPropagation();
30216            this.slideIn();
30217         }else{
30218            e.stopPropagation();
30219            this.slideOut();
30220         }
30221     },
30222
30223     /**
30224      * Collapses this region.
30225      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30226      */
30227     collapse : function(skipAnim, skipCheck){
30228         if(this.collapsed) {
30229             return;
30230         }
30231         
30232         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30233             
30234             this.collapsed = true;
30235             if(this.split){
30236                 this.split.el.hide();
30237             }
30238             if(this.config.animate && skipAnim !== true){
30239                 this.fireEvent("invalidated", this);
30240                 this.animateCollapse();
30241             }else{
30242                 this.el.setLocation(-20000,-20000);
30243                 this.el.hide();
30244                 this.collapsedEl.show();
30245                 this.fireEvent("collapsed", this);
30246                 this.fireEvent("invalidated", this);
30247             }
30248         }
30249         
30250     },
30251
30252     animateCollapse : function(){
30253         // overridden
30254     },
30255
30256     /**
30257      * Expands this region if it was previously collapsed.
30258      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30259      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30260      */
30261     expand : function(e, skipAnim){
30262         if(e) {
30263             e.stopPropagation();
30264         }
30265         if(!this.collapsed || this.el.hasActiveFx()) {
30266             return;
30267         }
30268         if(this.isSlid){
30269             this.afterSlideIn();
30270             skipAnim = true;
30271         }
30272         this.collapsed = false;
30273         if(this.config.animate && skipAnim !== true){
30274             this.animateExpand();
30275         }else{
30276             this.el.show();
30277             if(this.split){
30278                 this.split.el.show();
30279             }
30280             this.collapsedEl.setLocation(-2000,-2000);
30281             this.collapsedEl.hide();
30282             this.fireEvent("invalidated", this);
30283             this.fireEvent("expanded", this);
30284         }
30285     },
30286
30287     animateExpand : function(){
30288         // overridden
30289     },
30290
30291     initTabs : function()
30292     {
30293         this.bodyEl.setStyle("overflow", "hidden");
30294         var ts = new Roo.TabPanel(
30295                 this.bodyEl.dom,
30296                 {
30297                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30298                     disableTooltips: this.config.disableTabTips,
30299                     toolbar : this.config.toolbar
30300                 }
30301         );
30302         if(this.config.hideTabs){
30303             ts.stripWrap.setDisplayed(false);
30304         }
30305         this.tabs = ts;
30306         ts.resizeTabs = this.config.resizeTabs === true;
30307         ts.minTabWidth = this.config.minTabWidth || 40;
30308         ts.maxTabWidth = this.config.maxTabWidth || 250;
30309         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30310         ts.monitorResize = false;
30311         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30312         ts.bodyEl.addClass('x-layout-tabs-body');
30313         this.panels.each(this.initPanelAsTab, this);
30314     },
30315
30316     initPanelAsTab : function(panel){
30317         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30318                     this.config.closeOnTab && panel.isClosable());
30319         if(panel.tabTip !== undefined){
30320             ti.setTooltip(panel.tabTip);
30321         }
30322         ti.on("activate", function(){
30323               this.setActivePanel(panel);
30324         }, this);
30325         if(this.config.closeOnTab){
30326             ti.on("beforeclose", function(t, e){
30327                 e.cancel = true;
30328                 this.remove(panel);
30329             }, this);
30330         }
30331         return ti;
30332     },
30333
30334     updatePanelTitle : function(panel, title){
30335         if(this.activePanel == panel){
30336             this.updateTitle(title);
30337         }
30338         if(this.tabs){
30339             var ti = this.tabs.getTab(panel.getEl().id);
30340             ti.setText(title);
30341             if(panel.tabTip !== undefined){
30342                 ti.setTooltip(panel.tabTip);
30343             }
30344         }
30345     },
30346
30347     updateTitle : function(title){
30348         if(this.titleTextEl && !this.config.title){
30349             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30350         }
30351     },
30352
30353     setActivePanel : function(panel){
30354         panel = this.getPanel(panel);
30355         if(this.activePanel && this.activePanel != panel){
30356             this.activePanel.setActiveState(false);
30357         }
30358         this.activePanel = panel;
30359         panel.setActiveState(true);
30360         if(this.panelSize){
30361             panel.setSize(this.panelSize.width, this.panelSize.height);
30362         }
30363         if(this.closeBtn){
30364             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30365         }
30366         this.updateTitle(panel.getTitle());
30367         if(this.tabs){
30368             this.fireEvent("invalidated", this);
30369         }
30370         this.fireEvent("panelactivated", this, panel);
30371     },
30372
30373     /**
30374      * Shows the specified panel.
30375      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30376      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30377      */
30378     showPanel : function(panel)
30379     {
30380         panel = this.getPanel(panel);
30381         if(panel){
30382             if(this.tabs){
30383                 var tab = this.tabs.getTab(panel.getEl().id);
30384                 if(tab.isHidden()){
30385                     this.tabs.unhideTab(tab.id);
30386                 }
30387                 tab.activate();
30388             }else{
30389                 this.setActivePanel(panel);
30390             }
30391         }
30392         return panel;
30393     },
30394
30395     /**
30396      * Get the active panel for this region.
30397      * @return {Roo.ContentPanel} The active panel or null
30398      */
30399     getActivePanel : function(){
30400         return this.activePanel;
30401     },
30402
30403     validateVisibility : function(){
30404         if(this.panels.getCount() < 1){
30405             this.updateTitle("&#160;");
30406             this.closeBtn.hide();
30407             this.hide();
30408         }else{
30409             if(!this.isVisible()){
30410                 this.show();
30411             }
30412         }
30413     },
30414
30415     /**
30416      * Adds the passed ContentPanel(s) to this region.
30417      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30418      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30419      */
30420     add : function(panel){
30421         if(arguments.length > 1){
30422             for(var i = 0, len = arguments.length; i < len; i++) {
30423                 this.add(arguments[i]);
30424             }
30425             return null;
30426         }
30427         if(this.hasPanel(panel)){
30428             this.showPanel(panel);
30429             return panel;
30430         }
30431         panel.setRegion(this);
30432         this.panels.add(panel);
30433         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30434             this.bodyEl.dom.appendChild(panel.getEl().dom);
30435             if(panel.background !== true){
30436                 this.setActivePanel(panel);
30437             }
30438             this.fireEvent("paneladded", this, panel);
30439             return panel;
30440         }
30441         if(!this.tabs){
30442             this.initTabs();
30443         }else{
30444             this.initPanelAsTab(panel);
30445         }
30446         if(panel.background !== true){
30447             this.tabs.activate(panel.getEl().id);
30448         }
30449         this.fireEvent("paneladded", this, panel);
30450         return panel;
30451     },
30452
30453     /**
30454      * Hides the tab for the specified panel.
30455      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30456      */
30457     hidePanel : function(panel){
30458         if(this.tabs && (panel = this.getPanel(panel))){
30459             this.tabs.hideTab(panel.getEl().id);
30460         }
30461     },
30462
30463     /**
30464      * Unhides the tab for a previously hidden panel.
30465      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30466      */
30467     unhidePanel : function(panel){
30468         if(this.tabs && (panel = this.getPanel(panel))){
30469             this.tabs.unhideTab(panel.getEl().id);
30470         }
30471     },
30472
30473     clearPanels : function(){
30474         while(this.panels.getCount() > 0){
30475              this.remove(this.panels.first());
30476         }
30477     },
30478
30479     /**
30480      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30481      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30482      * @param {Boolean} preservePanel Overrides the config preservePanel option
30483      * @return {Roo.ContentPanel} The panel that was removed
30484      */
30485     remove : function(panel, preservePanel){
30486         panel = this.getPanel(panel);
30487         if(!panel){
30488             return null;
30489         }
30490         var e = {};
30491         this.fireEvent("beforeremove", this, panel, e);
30492         if(e.cancel === true){
30493             return null;
30494         }
30495         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30496         var panelId = panel.getId();
30497         this.panels.removeKey(panelId);
30498         if(preservePanel){
30499             document.body.appendChild(panel.getEl().dom);
30500         }
30501         if(this.tabs){
30502             this.tabs.removeTab(panel.getEl().id);
30503         }else if (!preservePanel){
30504             this.bodyEl.dom.removeChild(panel.getEl().dom);
30505         }
30506         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30507             var p = this.panels.first();
30508             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30509             tempEl.appendChild(p.getEl().dom);
30510             this.bodyEl.update("");
30511             this.bodyEl.dom.appendChild(p.getEl().dom);
30512             tempEl = null;
30513             this.updateTitle(p.getTitle());
30514             this.tabs = null;
30515             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30516             this.setActivePanel(p);
30517         }
30518         panel.setRegion(null);
30519         if(this.activePanel == panel){
30520             this.activePanel = null;
30521         }
30522         if(this.config.autoDestroy !== false && preservePanel !== true){
30523             try{panel.destroy();}catch(e){}
30524         }
30525         this.fireEvent("panelremoved", this, panel);
30526         return panel;
30527     },
30528
30529     /**
30530      * Returns the TabPanel component used by this region
30531      * @return {Roo.TabPanel}
30532      */
30533     getTabs : function(){
30534         return this.tabs;
30535     },
30536
30537     createTool : function(parentEl, className){
30538         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30539             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30540         btn.addClassOnOver("x-layout-tools-button-over");
30541         return btn;
30542     }
30543 });/*
30544  * Based on:
30545  * Ext JS Library 1.1.1
30546  * Copyright(c) 2006-2007, Ext JS, LLC.
30547  *
30548  * Originally Released Under LGPL - original licence link has changed is not relivant.
30549  *
30550  * Fork - LGPL
30551  * <script type="text/javascript">
30552  */
30553  
30554
30555
30556 /**
30557  * @class Roo.SplitLayoutRegion
30558  * @extends Roo.LayoutRegion
30559  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30560  */
30561 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30562     this.cursor = cursor;
30563     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30564 };
30565
30566 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30567     splitTip : "Drag to resize.",
30568     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30569     useSplitTips : false,
30570
30571     applyConfig : function(config){
30572         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30573         if(config.split){
30574             if(!this.split){
30575                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30576                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30577                 /** The SplitBar for this region 
30578                 * @type Roo.SplitBar */
30579                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30580                 this.split.on("moved", this.onSplitMove, this);
30581                 this.split.useShim = config.useShim === true;
30582                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30583                 if(this.useSplitTips){
30584                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30585                 }
30586                 if(config.collapsible){
30587                     this.split.el.on("dblclick", this.collapse,  this);
30588                 }
30589             }
30590             if(typeof config.minSize != "undefined"){
30591                 this.split.minSize = config.minSize;
30592             }
30593             if(typeof config.maxSize != "undefined"){
30594                 this.split.maxSize = config.maxSize;
30595             }
30596             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30597                 this.hideSplitter();
30598             }
30599         }
30600     },
30601
30602     getHMaxSize : function(){
30603          var cmax = this.config.maxSize || 10000;
30604          var center = this.mgr.getRegion("center");
30605          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30606     },
30607
30608     getVMaxSize : function(){
30609          var cmax = this.config.maxSize || 10000;
30610          var center = this.mgr.getRegion("center");
30611          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30612     },
30613
30614     onSplitMove : function(split, newSize){
30615         this.fireEvent("resized", this, newSize);
30616     },
30617     
30618     /** 
30619      * Returns the {@link Roo.SplitBar} for this region.
30620      * @return {Roo.SplitBar}
30621      */
30622     getSplitBar : function(){
30623         return this.split;
30624     },
30625     
30626     hide : function(){
30627         this.hideSplitter();
30628         Roo.SplitLayoutRegion.superclass.hide.call(this);
30629     },
30630
30631     hideSplitter : function(){
30632         if(this.split){
30633             this.split.el.setLocation(-2000,-2000);
30634             this.split.el.hide();
30635         }
30636     },
30637
30638     show : function(){
30639         if(this.split){
30640             this.split.el.show();
30641         }
30642         Roo.SplitLayoutRegion.superclass.show.call(this);
30643     },
30644     
30645     beforeSlide: function(){
30646         if(Roo.isGecko){// firefox overflow auto bug workaround
30647             this.bodyEl.clip();
30648             if(this.tabs) {
30649                 this.tabs.bodyEl.clip();
30650             }
30651             if(this.activePanel){
30652                 this.activePanel.getEl().clip();
30653                 
30654                 if(this.activePanel.beforeSlide){
30655                     this.activePanel.beforeSlide();
30656                 }
30657             }
30658         }
30659     },
30660     
30661     afterSlide : function(){
30662         if(Roo.isGecko){// firefox overflow auto bug workaround
30663             this.bodyEl.unclip();
30664             if(this.tabs) {
30665                 this.tabs.bodyEl.unclip();
30666             }
30667             if(this.activePanel){
30668                 this.activePanel.getEl().unclip();
30669                 if(this.activePanel.afterSlide){
30670                     this.activePanel.afterSlide();
30671                 }
30672             }
30673         }
30674     },
30675
30676     initAutoHide : function(){
30677         if(this.autoHide !== false){
30678             if(!this.autoHideHd){
30679                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30680                 this.autoHideHd = {
30681                     "mouseout": function(e){
30682                         if(!e.within(this.el, true)){
30683                             st.delay(500);
30684                         }
30685                     },
30686                     "mouseover" : function(e){
30687                         st.cancel();
30688                     },
30689                     scope : this
30690                 };
30691             }
30692             this.el.on(this.autoHideHd);
30693         }
30694     },
30695
30696     clearAutoHide : function(){
30697         if(this.autoHide !== false){
30698             this.el.un("mouseout", this.autoHideHd.mouseout);
30699             this.el.un("mouseover", this.autoHideHd.mouseover);
30700         }
30701     },
30702
30703     clearMonitor : function(){
30704         Roo.get(document).un("click", this.slideInIf, this);
30705     },
30706
30707     // these names are backwards but not changed for compat
30708     slideOut : function(){
30709         if(this.isSlid || this.el.hasActiveFx()){
30710             return;
30711         }
30712         this.isSlid = true;
30713         if(this.collapseBtn){
30714             this.collapseBtn.hide();
30715         }
30716         this.closeBtnState = this.closeBtn.getStyle('display');
30717         this.closeBtn.hide();
30718         if(this.stickBtn){
30719             this.stickBtn.show();
30720         }
30721         this.el.show();
30722         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30723         this.beforeSlide();
30724         this.el.setStyle("z-index", 10001);
30725         this.el.slideIn(this.getSlideAnchor(), {
30726             callback: function(){
30727                 this.afterSlide();
30728                 this.initAutoHide();
30729                 Roo.get(document).on("click", this.slideInIf, this);
30730                 this.fireEvent("slideshow", this);
30731             },
30732             scope: this,
30733             block: true
30734         });
30735     },
30736
30737     afterSlideIn : function(){
30738         this.clearAutoHide();
30739         this.isSlid = false;
30740         this.clearMonitor();
30741         this.el.setStyle("z-index", "");
30742         if(this.collapseBtn){
30743             this.collapseBtn.show();
30744         }
30745         this.closeBtn.setStyle('display', this.closeBtnState);
30746         if(this.stickBtn){
30747             this.stickBtn.hide();
30748         }
30749         this.fireEvent("slidehide", this);
30750     },
30751
30752     slideIn : function(cb){
30753         if(!this.isSlid || this.el.hasActiveFx()){
30754             Roo.callback(cb);
30755             return;
30756         }
30757         this.isSlid = false;
30758         this.beforeSlide();
30759         this.el.slideOut(this.getSlideAnchor(), {
30760             callback: function(){
30761                 this.el.setLeftTop(-10000, -10000);
30762                 this.afterSlide();
30763                 this.afterSlideIn();
30764                 Roo.callback(cb);
30765             },
30766             scope: this,
30767             block: true
30768         });
30769     },
30770     
30771     slideInIf : function(e){
30772         if(!e.within(this.el)){
30773             this.slideIn();
30774         }
30775     },
30776
30777     animateCollapse : function(){
30778         this.beforeSlide();
30779         this.el.setStyle("z-index", 20000);
30780         var anchor = this.getSlideAnchor();
30781         this.el.slideOut(anchor, {
30782             callback : function(){
30783                 this.el.setStyle("z-index", "");
30784                 this.collapsedEl.slideIn(anchor, {duration:.3});
30785                 this.afterSlide();
30786                 this.el.setLocation(-10000,-10000);
30787                 this.el.hide();
30788                 this.fireEvent("collapsed", this);
30789             },
30790             scope: this,
30791             block: true
30792         });
30793     },
30794
30795     animateExpand : function(){
30796         this.beforeSlide();
30797         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30798         this.el.setStyle("z-index", 20000);
30799         this.collapsedEl.hide({
30800             duration:.1
30801         });
30802         this.el.slideIn(this.getSlideAnchor(), {
30803             callback : function(){
30804                 this.el.setStyle("z-index", "");
30805                 this.afterSlide();
30806                 if(this.split){
30807                     this.split.el.show();
30808                 }
30809                 this.fireEvent("invalidated", this);
30810                 this.fireEvent("expanded", this);
30811             },
30812             scope: this,
30813             block: true
30814         });
30815     },
30816
30817     anchors : {
30818         "west" : "left",
30819         "east" : "right",
30820         "north" : "top",
30821         "south" : "bottom"
30822     },
30823
30824     sanchors : {
30825         "west" : "l",
30826         "east" : "r",
30827         "north" : "t",
30828         "south" : "b"
30829     },
30830
30831     canchors : {
30832         "west" : "tl-tr",
30833         "east" : "tr-tl",
30834         "north" : "tl-bl",
30835         "south" : "bl-tl"
30836     },
30837
30838     getAnchor : function(){
30839         return this.anchors[this.position];
30840     },
30841
30842     getCollapseAnchor : function(){
30843         return this.canchors[this.position];
30844     },
30845
30846     getSlideAnchor : function(){
30847         return this.sanchors[this.position];
30848     },
30849
30850     getAlignAdj : function(){
30851         var cm = this.cmargins;
30852         switch(this.position){
30853             case "west":
30854                 return [0, 0];
30855             break;
30856             case "east":
30857                 return [0, 0];
30858             break;
30859             case "north":
30860                 return [0, 0];
30861             break;
30862             case "south":
30863                 return [0, 0];
30864             break;
30865         }
30866     },
30867
30868     getExpandAdj : function(){
30869         var c = this.collapsedEl, cm = this.cmargins;
30870         switch(this.position){
30871             case "west":
30872                 return [-(cm.right+c.getWidth()+cm.left), 0];
30873             break;
30874             case "east":
30875                 return [cm.right+c.getWidth()+cm.left, 0];
30876             break;
30877             case "north":
30878                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30879             break;
30880             case "south":
30881                 return [0, cm.top+cm.bottom+c.getHeight()];
30882             break;
30883         }
30884     }
30885 });/*
30886  * Based on:
30887  * Ext JS Library 1.1.1
30888  * Copyright(c) 2006-2007, Ext JS, LLC.
30889  *
30890  * Originally Released Under LGPL - original licence link has changed is not relivant.
30891  *
30892  * Fork - LGPL
30893  * <script type="text/javascript">
30894  */
30895 /*
30896  * These classes are private internal classes
30897  */
30898 Roo.CenterLayoutRegion = function(mgr, config){
30899     Roo.LayoutRegion.call(this, mgr, config, "center");
30900     this.visible = true;
30901     this.minWidth = config.minWidth || 20;
30902     this.minHeight = config.minHeight || 20;
30903 };
30904
30905 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30906     hide : function(){
30907         // center panel can't be hidden
30908     },
30909     
30910     show : function(){
30911         // center panel can't be hidden
30912     },
30913     
30914     getMinWidth: function(){
30915         return this.minWidth;
30916     },
30917     
30918     getMinHeight: function(){
30919         return this.minHeight;
30920     }
30921 });
30922
30923
30924 Roo.NorthLayoutRegion = function(mgr, config){
30925     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30926     if(this.split){
30927         this.split.placement = Roo.SplitBar.TOP;
30928         this.split.orientation = Roo.SplitBar.VERTICAL;
30929         this.split.el.addClass("x-layout-split-v");
30930     }
30931     var size = config.initialSize || config.height;
30932     if(typeof size != "undefined"){
30933         this.el.setHeight(size);
30934     }
30935 };
30936 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30937     orientation: Roo.SplitBar.VERTICAL,
30938     getBox : function(){
30939         if(this.collapsed){
30940             return this.collapsedEl.getBox();
30941         }
30942         var box = this.el.getBox();
30943         if(this.split){
30944             box.height += this.split.el.getHeight();
30945         }
30946         return box;
30947     },
30948     
30949     updateBox : function(box){
30950         if(this.split && !this.collapsed){
30951             box.height -= this.split.el.getHeight();
30952             this.split.el.setLeft(box.x);
30953             this.split.el.setTop(box.y+box.height);
30954             this.split.el.setWidth(box.width);
30955         }
30956         if(this.collapsed){
30957             this.updateBody(box.width, null);
30958         }
30959         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30960     }
30961 });
30962
30963 Roo.SouthLayoutRegion = function(mgr, config){
30964     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30965     if(this.split){
30966         this.split.placement = Roo.SplitBar.BOTTOM;
30967         this.split.orientation = Roo.SplitBar.VERTICAL;
30968         this.split.el.addClass("x-layout-split-v");
30969     }
30970     var size = config.initialSize || config.height;
30971     if(typeof size != "undefined"){
30972         this.el.setHeight(size);
30973     }
30974 };
30975 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30976     orientation: Roo.SplitBar.VERTICAL,
30977     getBox : function(){
30978         if(this.collapsed){
30979             return this.collapsedEl.getBox();
30980         }
30981         var box = this.el.getBox();
30982         if(this.split){
30983             var sh = this.split.el.getHeight();
30984             box.height += sh;
30985             box.y -= sh;
30986         }
30987         return box;
30988     },
30989     
30990     updateBox : function(box){
30991         if(this.split && !this.collapsed){
30992             var sh = this.split.el.getHeight();
30993             box.height -= sh;
30994             box.y += sh;
30995             this.split.el.setLeft(box.x);
30996             this.split.el.setTop(box.y-sh);
30997             this.split.el.setWidth(box.width);
30998         }
30999         if(this.collapsed){
31000             this.updateBody(box.width, null);
31001         }
31002         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31003     }
31004 });
31005
31006 Roo.EastLayoutRegion = function(mgr, config){
31007     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31008     if(this.split){
31009         this.split.placement = Roo.SplitBar.RIGHT;
31010         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31011         this.split.el.addClass("x-layout-split-h");
31012     }
31013     var size = config.initialSize || config.width;
31014     if(typeof size != "undefined"){
31015         this.el.setWidth(size);
31016     }
31017 };
31018 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31019     orientation: Roo.SplitBar.HORIZONTAL,
31020     getBox : function(){
31021         if(this.collapsed){
31022             return this.collapsedEl.getBox();
31023         }
31024         var box = this.el.getBox();
31025         if(this.split){
31026             var sw = this.split.el.getWidth();
31027             box.width += sw;
31028             box.x -= sw;
31029         }
31030         return box;
31031     },
31032
31033     updateBox : function(box){
31034         if(this.split && !this.collapsed){
31035             var sw = this.split.el.getWidth();
31036             box.width -= sw;
31037             this.split.el.setLeft(box.x);
31038             this.split.el.setTop(box.y);
31039             this.split.el.setHeight(box.height);
31040             box.x += sw;
31041         }
31042         if(this.collapsed){
31043             this.updateBody(null, box.height);
31044         }
31045         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31046     }
31047 });
31048
31049 Roo.WestLayoutRegion = function(mgr, config){
31050     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31051     if(this.split){
31052         this.split.placement = Roo.SplitBar.LEFT;
31053         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31054         this.split.el.addClass("x-layout-split-h");
31055     }
31056     var size = config.initialSize || config.width;
31057     if(typeof size != "undefined"){
31058         this.el.setWidth(size);
31059     }
31060 };
31061 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31062     orientation: Roo.SplitBar.HORIZONTAL,
31063     getBox : function(){
31064         if(this.collapsed){
31065             return this.collapsedEl.getBox();
31066         }
31067         var box = this.el.getBox();
31068         if(this.split){
31069             box.width += this.split.el.getWidth();
31070         }
31071         return box;
31072     },
31073     
31074     updateBox : function(box){
31075         if(this.split && !this.collapsed){
31076             var sw = this.split.el.getWidth();
31077             box.width -= sw;
31078             this.split.el.setLeft(box.x+box.width);
31079             this.split.el.setTop(box.y);
31080             this.split.el.setHeight(box.height);
31081         }
31082         if(this.collapsed){
31083             this.updateBody(null, box.height);
31084         }
31085         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31086     }
31087 });
31088 /*
31089  * Based on:
31090  * Ext JS Library 1.1.1
31091  * Copyright(c) 2006-2007, Ext JS, LLC.
31092  *
31093  * Originally Released Under LGPL - original licence link has changed is not relivant.
31094  *
31095  * Fork - LGPL
31096  * <script type="text/javascript">
31097  */
31098  
31099  
31100 /*
31101  * Private internal class for reading and applying state
31102  */
31103 Roo.LayoutStateManager = function(layout){
31104      // default empty state
31105      this.state = {
31106         north: {},
31107         south: {},
31108         east: {},
31109         west: {}       
31110     };
31111 };
31112
31113 Roo.LayoutStateManager.prototype = {
31114     init : function(layout, provider){
31115         this.provider = provider;
31116         var state = provider.get(layout.id+"-layout-state");
31117         if(state){
31118             var wasUpdating = layout.isUpdating();
31119             if(!wasUpdating){
31120                 layout.beginUpdate();
31121             }
31122             for(var key in state){
31123                 if(typeof state[key] != "function"){
31124                     var rstate = state[key];
31125                     var r = layout.getRegion(key);
31126                     if(r && rstate){
31127                         if(rstate.size){
31128                             r.resizeTo(rstate.size);
31129                         }
31130                         if(rstate.collapsed == true){
31131                             r.collapse(true);
31132                         }else{
31133                             r.expand(null, true);
31134                         }
31135                     }
31136                 }
31137             }
31138             if(!wasUpdating){
31139                 layout.endUpdate();
31140             }
31141             this.state = state; 
31142         }
31143         this.layout = layout;
31144         layout.on("regionresized", this.onRegionResized, this);
31145         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31146         layout.on("regionexpanded", this.onRegionExpanded, this);
31147     },
31148     
31149     storeState : function(){
31150         this.provider.set(this.layout.id+"-layout-state", this.state);
31151     },
31152     
31153     onRegionResized : function(region, newSize){
31154         this.state[region.getPosition()].size = newSize;
31155         this.storeState();
31156     },
31157     
31158     onRegionCollapsed : function(region){
31159         this.state[region.getPosition()].collapsed = true;
31160         this.storeState();
31161     },
31162     
31163     onRegionExpanded : function(region){
31164         this.state[region.getPosition()].collapsed = false;
31165         this.storeState();
31166     }
31167 };/*
31168  * Based on:
31169  * Ext JS Library 1.1.1
31170  * Copyright(c) 2006-2007, Ext JS, LLC.
31171  *
31172  * Originally Released Under LGPL - original licence link has changed is not relivant.
31173  *
31174  * Fork - LGPL
31175  * <script type="text/javascript">
31176  */
31177 /**
31178  * @class Roo.ContentPanel
31179  * @extends Roo.util.Observable
31180  * A basic ContentPanel element.
31181  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31182  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31183  * @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
31184  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31185  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31186  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31187  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31188  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31189  * @cfg {String} title          The title for this panel
31190  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31191  * @cfg {String} url            Calls {@link #setUrl} with this value
31192  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31193  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31194  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31195  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31196  * @cfg {String}    style  Extra style to add to the content panel 
31197
31198  * @constructor
31199  * Create a new ContentPanel.
31200  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31201  * @param {String/Object} config A string to set only the title or a config object
31202  * @param {String} content (optional) Set the HTML content for this panel
31203  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31204  */
31205 Roo.ContentPanel = function(el, config, content){
31206     
31207      
31208     /*
31209     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31210         config = el;
31211         el = Roo.id();
31212     }
31213     if (config && config.parentLayout) { 
31214         el = config.parentLayout.el.createChild(); 
31215     }
31216     */
31217     if(el.autoCreate){ // xtype is available if this is called from factory
31218         config = el;
31219         el = Roo.id();
31220     }
31221     this.el = Roo.get(el);
31222     if(!this.el && config && config.autoCreate){
31223         if(typeof config.autoCreate == "object"){
31224             if(!config.autoCreate.id){
31225                 config.autoCreate.id = config.id||el;
31226             }
31227             this.el = Roo.DomHelper.append(document.body,
31228                         config.autoCreate, true);
31229         }else{
31230             this.el = Roo.DomHelper.append(document.body,
31231                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31232         }
31233     }
31234     
31235     
31236     this.closable = false;
31237     this.loaded = false;
31238     this.active = false;
31239     if(typeof config == "string"){
31240         this.title = config;
31241     }else{
31242         Roo.apply(this, config);
31243     }
31244     
31245     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31246         this.wrapEl = this.el.wrap();
31247         this.toolbar.container = this.el.insertSibling(false, 'before');
31248         this.toolbar = new Roo.Toolbar(this.toolbar);
31249     }
31250     
31251     // xtype created footer. - not sure if will work as we normally have to render first..
31252     if (this.footer && !this.footer.el && this.footer.xtype) {
31253         if (!this.wrapEl) {
31254             this.wrapEl = this.el.wrap();
31255         }
31256     
31257         this.footer.container = this.wrapEl.createChild();
31258          
31259         this.footer = Roo.factory(this.footer, Roo);
31260         
31261     }
31262     
31263     if(this.resizeEl){
31264         this.resizeEl = Roo.get(this.resizeEl, true);
31265     }else{
31266         this.resizeEl = this.el;
31267     }
31268     // handle view.xtype
31269     
31270  
31271     
31272     
31273     this.addEvents({
31274         /**
31275          * @event activate
31276          * Fires when this panel is activated. 
31277          * @param {Roo.ContentPanel} this
31278          */
31279         "activate" : true,
31280         /**
31281          * @event deactivate
31282          * Fires when this panel is activated. 
31283          * @param {Roo.ContentPanel} this
31284          */
31285         "deactivate" : true,
31286
31287         /**
31288          * @event resize
31289          * Fires when this panel is resized if fitToFrame is true.
31290          * @param {Roo.ContentPanel} this
31291          * @param {Number} width The width after any component adjustments
31292          * @param {Number} height The height after any component adjustments
31293          */
31294         "resize" : true,
31295         
31296          /**
31297          * @event render
31298          * Fires when this tab is created
31299          * @param {Roo.ContentPanel} this
31300          */
31301         "render" : true
31302          
31303         
31304     });
31305     
31306
31307     
31308     
31309     if(this.autoScroll){
31310         this.resizeEl.setStyle("overflow", "auto");
31311     } else {
31312         // fix randome scrolling
31313         this.el.on('scroll', function() {
31314             Roo.log('fix random scolling');
31315             this.scrollTo('top',0); 
31316         });
31317     }
31318     content = content || this.content;
31319     if(content){
31320         this.setContent(content);
31321     }
31322     if(config && config.url){
31323         this.setUrl(this.url, this.params, this.loadOnce);
31324     }
31325     
31326     
31327     
31328     Roo.ContentPanel.superclass.constructor.call(this);
31329     
31330     if (this.view && typeof(this.view.xtype) != 'undefined') {
31331         this.view.el = this.el.appendChild(document.createElement("div"));
31332         this.view = Roo.factory(this.view); 
31333         this.view.render  &&  this.view.render(false, '');  
31334     }
31335     
31336     
31337     this.fireEvent('render', this);
31338 };
31339
31340 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31341     tabTip:'',
31342     setRegion : function(region){
31343         this.region = region;
31344         if(region){
31345            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31346         }else{
31347            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31348         } 
31349     },
31350     
31351     /**
31352      * Returns the toolbar for this Panel if one was configured. 
31353      * @return {Roo.Toolbar} 
31354      */
31355     getToolbar : function(){
31356         return this.toolbar;
31357     },
31358     
31359     setActiveState : function(active){
31360         this.active = active;
31361         if(!active){
31362             this.fireEvent("deactivate", this);
31363         }else{
31364             this.fireEvent("activate", this);
31365         }
31366     },
31367     /**
31368      * Updates this panel's element
31369      * @param {String} content The new content
31370      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31371     */
31372     setContent : function(content, loadScripts){
31373         this.el.update(content, loadScripts);
31374     },
31375
31376     ignoreResize : function(w, h){
31377         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31378             return true;
31379         }else{
31380             this.lastSize = {width: w, height: h};
31381             return false;
31382         }
31383     },
31384     /**
31385      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31386      * @return {Roo.UpdateManager} The UpdateManager
31387      */
31388     getUpdateManager : function(){
31389         return this.el.getUpdateManager();
31390     },
31391      /**
31392      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31393      * @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:
31394 <pre><code>
31395 panel.load({
31396     url: "your-url.php",
31397     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31398     callback: yourFunction,
31399     scope: yourObject, //(optional scope)
31400     discardUrl: false,
31401     nocache: false,
31402     text: "Loading...",
31403     timeout: 30,
31404     scripts: false
31405 });
31406 </code></pre>
31407      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31408      * 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.
31409      * @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}
31410      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31411      * @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.
31412      * @return {Roo.ContentPanel} this
31413      */
31414     load : function(){
31415         var um = this.el.getUpdateManager();
31416         um.update.apply(um, arguments);
31417         return this;
31418     },
31419
31420
31421     /**
31422      * 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.
31423      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31424      * @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)
31425      * @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)
31426      * @return {Roo.UpdateManager} The UpdateManager
31427      */
31428     setUrl : function(url, params, loadOnce){
31429         if(this.refreshDelegate){
31430             this.removeListener("activate", this.refreshDelegate);
31431         }
31432         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31433         this.on("activate", this.refreshDelegate);
31434         return this.el.getUpdateManager();
31435     },
31436     
31437     _handleRefresh : function(url, params, loadOnce){
31438         if(!loadOnce || !this.loaded){
31439             var updater = this.el.getUpdateManager();
31440             updater.update(url, params, this._setLoaded.createDelegate(this));
31441         }
31442     },
31443     
31444     _setLoaded : function(){
31445         this.loaded = true;
31446     }, 
31447     
31448     /**
31449      * Returns this panel's id
31450      * @return {String} 
31451      */
31452     getId : function(){
31453         return this.el.id;
31454     },
31455     
31456     /** 
31457      * Returns this panel's element - used by regiosn to add.
31458      * @return {Roo.Element} 
31459      */
31460     getEl : function(){
31461         return this.wrapEl || this.el;
31462     },
31463     
31464     adjustForComponents : function(width, height)
31465     {
31466         //Roo.log('adjustForComponents ');
31467         if(this.resizeEl != this.el){
31468             width -= this.el.getFrameWidth('lr');
31469             height -= this.el.getFrameWidth('tb');
31470         }
31471         if(this.toolbar){
31472             var te = this.toolbar.getEl();
31473             height -= te.getHeight();
31474             te.setWidth(width);
31475         }
31476         if(this.footer){
31477             var te = this.footer.getEl();
31478             //Roo.log("footer:" + te.getHeight());
31479             
31480             height -= te.getHeight();
31481             te.setWidth(width);
31482         }
31483         
31484         
31485         if(this.adjustments){
31486             width += this.adjustments[0];
31487             height += this.adjustments[1];
31488         }
31489         return {"width": width, "height": height};
31490     },
31491     
31492     setSize : function(width, height){
31493         if(this.fitToFrame && !this.ignoreResize(width, height)){
31494             if(this.fitContainer && this.resizeEl != this.el){
31495                 this.el.setSize(width, height);
31496             }
31497             var size = this.adjustForComponents(width, height);
31498             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31499             this.fireEvent('resize', this, size.width, size.height);
31500         }
31501     },
31502     
31503     /**
31504      * Returns this panel's title
31505      * @return {String} 
31506      */
31507     getTitle : function(){
31508         return this.title;
31509     },
31510     
31511     /**
31512      * Set this panel's title
31513      * @param {String} title
31514      */
31515     setTitle : function(title){
31516         this.title = title;
31517         if(this.region){
31518             this.region.updatePanelTitle(this, title);
31519         }
31520     },
31521     
31522     /**
31523      * Returns true is this panel was configured to be closable
31524      * @return {Boolean} 
31525      */
31526     isClosable : function(){
31527         return this.closable;
31528     },
31529     
31530     beforeSlide : function(){
31531         this.el.clip();
31532         this.resizeEl.clip();
31533     },
31534     
31535     afterSlide : function(){
31536         this.el.unclip();
31537         this.resizeEl.unclip();
31538     },
31539     
31540     /**
31541      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31542      *   Will fail silently if the {@link #setUrl} method has not been called.
31543      *   This does not activate the panel, just updates its content.
31544      */
31545     refresh : function(){
31546         if(this.refreshDelegate){
31547            this.loaded = false;
31548            this.refreshDelegate();
31549         }
31550     },
31551     
31552     /**
31553      * Destroys this panel
31554      */
31555     destroy : function(){
31556         this.el.removeAllListeners();
31557         var tempEl = document.createElement("span");
31558         tempEl.appendChild(this.el.dom);
31559         tempEl.innerHTML = "";
31560         this.el.remove();
31561         this.el = null;
31562     },
31563     
31564     /**
31565      * form - if the content panel contains a form - this is a reference to it.
31566      * @type {Roo.form.Form}
31567      */
31568     form : false,
31569     /**
31570      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31571      *    This contains a reference to it.
31572      * @type {Roo.View}
31573      */
31574     view : false,
31575     
31576       /**
31577      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31578      * <pre><code>
31579
31580 layout.addxtype({
31581        xtype : 'Form',
31582        items: [ .... ]
31583    }
31584 );
31585
31586 </code></pre>
31587      * @param {Object} cfg Xtype definition of item to add.
31588      */
31589     
31590     addxtype : function(cfg) {
31591         // add form..
31592         if (cfg.xtype.match(/^Form$/)) {
31593             
31594             var el;
31595             //if (this.footer) {
31596             //    el = this.footer.container.insertSibling(false, 'before');
31597             //} else {
31598                 el = this.el.createChild();
31599             //}
31600
31601             this.form = new  Roo.form.Form(cfg);
31602             
31603             
31604             if ( this.form.allItems.length) {
31605                 this.form.render(el.dom);
31606             }
31607             return this.form;
31608         }
31609         // should only have one of theses..
31610         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31611             // views.. should not be just added - used named prop 'view''
31612             
31613             cfg.el = this.el.appendChild(document.createElement("div"));
31614             // factory?
31615             
31616             var ret = new Roo.factory(cfg);
31617              
31618              ret.render && ret.render(false, ''); // render blank..
31619             this.view = ret;
31620             return ret;
31621         }
31622         return false;
31623     }
31624 });
31625
31626 /**
31627  * @class Roo.GridPanel
31628  * @extends Roo.ContentPanel
31629  * @constructor
31630  * Create a new GridPanel.
31631  * @param {Roo.grid.Grid} grid The grid for this panel
31632  * @param {String/Object} config A string to set only the panel's title, or a config object
31633  */
31634 Roo.GridPanel = function(grid, config){
31635     
31636   
31637     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31638         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31639         
31640     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31641     
31642     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31643     
31644     if(this.toolbar){
31645         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31646     }
31647     // xtype created footer. - not sure if will work as we normally have to render first..
31648     if (this.footer && !this.footer.el && this.footer.xtype) {
31649         
31650         this.footer.container = this.grid.getView().getFooterPanel(true);
31651         this.footer.dataSource = this.grid.dataSource;
31652         this.footer = Roo.factory(this.footer, Roo);
31653         
31654     }
31655     
31656     grid.monitorWindowResize = false; // turn off autosizing
31657     grid.autoHeight = false;
31658     grid.autoWidth = false;
31659     this.grid = grid;
31660     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31661 };
31662
31663 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31664     getId : function(){
31665         return this.grid.id;
31666     },
31667     
31668     /**
31669      * Returns the grid for this panel
31670      * @return {Roo.grid.Grid} 
31671      */
31672     getGrid : function(){
31673         return this.grid;    
31674     },
31675     
31676     setSize : function(width, height){
31677         if(!this.ignoreResize(width, height)){
31678             var grid = this.grid;
31679             var size = this.adjustForComponents(width, height);
31680             grid.getGridEl().setSize(size.width, size.height);
31681             grid.autoSize();
31682         }
31683     },
31684     
31685     beforeSlide : function(){
31686         this.grid.getView().scroller.clip();
31687     },
31688     
31689     afterSlide : function(){
31690         this.grid.getView().scroller.unclip();
31691     },
31692     
31693     destroy : function(){
31694         this.grid.destroy();
31695         delete this.grid;
31696         Roo.GridPanel.superclass.destroy.call(this); 
31697     }
31698 });
31699
31700
31701 /**
31702  * @class Roo.NestedLayoutPanel
31703  * @extends Roo.ContentPanel
31704  * @constructor
31705  * Create a new NestedLayoutPanel.
31706  * 
31707  * 
31708  * @param {Roo.BorderLayout} layout The layout for this panel
31709  * @param {String/Object} config A string to set only the title or a config object
31710  */
31711 Roo.NestedLayoutPanel = function(layout, config)
31712 {
31713     // construct with only one argument..
31714     /* FIXME - implement nicer consturctors
31715     if (layout.layout) {
31716         config = layout;
31717         layout = config.layout;
31718         delete config.layout;
31719     }
31720     if (layout.xtype && !layout.getEl) {
31721         // then layout needs constructing..
31722         layout = Roo.factory(layout, Roo);
31723     }
31724     */
31725     
31726     
31727     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31728     
31729     layout.monitorWindowResize = false; // turn off autosizing
31730     this.layout = layout;
31731     this.layout.getEl().addClass("x-layout-nested-layout");
31732     
31733     
31734     
31735     
31736 };
31737
31738 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31739
31740     setSize : function(width, height){
31741         if(!this.ignoreResize(width, height)){
31742             var size = this.adjustForComponents(width, height);
31743             var el = this.layout.getEl();
31744             el.setSize(size.width, size.height);
31745             var touch = el.dom.offsetWidth;
31746             this.layout.layout();
31747             // ie requires a double layout on the first pass
31748             if(Roo.isIE && !this.initialized){
31749                 this.initialized = true;
31750                 this.layout.layout();
31751             }
31752         }
31753     },
31754     
31755     // activate all subpanels if not currently active..
31756     
31757     setActiveState : function(active){
31758         this.active = active;
31759         if(!active){
31760             this.fireEvent("deactivate", this);
31761             return;
31762         }
31763         
31764         this.fireEvent("activate", this);
31765         // not sure if this should happen before or after..
31766         if (!this.layout) {
31767             return; // should not happen..
31768         }
31769         var reg = false;
31770         for (var r in this.layout.regions) {
31771             reg = this.layout.getRegion(r);
31772             if (reg.getActivePanel()) {
31773                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31774                 reg.setActivePanel(reg.getActivePanel());
31775                 continue;
31776             }
31777             if (!reg.panels.length) {
31778                 continue;
31779             }
31780             reg.showPanel(reg.getPanel(0));
31781         }
31782         
31783         
31784         
31785         
31786     },
31787     
31788     /**
31789      * Returns the nested BorderLayout for this panel
31790      * @return {Roo.BorderLayout} 
31791      */
31792     getLayout : function(){
31793         return this.layout;
31794     },
31795     
31796      /**
31797      * Adds a xtype elements to the layout of the nested panel
31798      * <pre><code>
31799
31800 panel.addxtype({
31801        xtype : 'ContentPanel',
31802        region: 'west',
31803        items: [ .... ]
31804    }
31805 );
31806
31807 panel.addxtype({
31808         xtype : 'NestedLayoutPanel',
31809         region: 'west',
31810         layout: {
31811            center: { },
31812            west: { }   
31813         },
31814         items : [ ... list of content panels or nested layout panels.. ]
31815    }
31816 );
31817 </code></pre>
31818      * @param {Object} cfg Xtype definition of item to add.
31819      */
31820     addxtype : function(cfg) {
31821         return this.layout.addxtype(cfg);
31822     
31823     }
31824 });
31825
31826 Roo.ScrollPanel = function(el, config, content){
31827     config = config || {};
31828     config.fitToFrame = true;
31829     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31830     
31831     this.el.dom.style.overflow = "hidden";
31832     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31833     this.el.removeClass("x-layout-inactive-content");
31834     this.el.on("mousewheel", this.onWheel, this);
31835
31836     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31837     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31838     up.unselectable(); down.unselectable();
31839     up.on("click", this.scrollUp, this);
31840     down.on("click", this.scrollDown, this);
31841     up.addClassOnOver("x-scroller-btn-over");
31842     down.addClassOnOver("x-scroller-btn-over");
31843     up.addClassOnClick("x-scroller-btn-click");
31844     down.addClassOnClick("x-scroller-btn-click");
31845     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31846
31847     this.resizeEl = this.el;
31848     this.el = wrap; this.up = up; this.down = down;
31849 };
31850
31851 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31852     increment : 100,
31853     wheelIncrement : 5,
31854     scrollUp : function(){
31855         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31856     },
31857
31858     scrollDown : function(){
31859         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31860     },
31861
31862     afterScroll : function(){
31863         var el = this.resizeEl;
31864         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31865         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31866         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31867     },
31868
31869     setSize : function(){
31870         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31871         this.afterScroll();
31872     },
31873
31874     onWheel : function(e){
31875         var d = e.getWheelDelta();
31876         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31877         this.afterScroll();
31878         e.stopEvent();
31879     },
31880
31881     setContent : function(content, loadScripts){
31882         this.resizeEl.update(content, loadScripts);
31883     }
31884
31885 });
31886
31887
31888
31889
31890
31891
31892
31893
31894
31895 /**
31896  * @class Roo.TreePanel
31897  * @extends Roo.ContentPanel
31898  * @constructor
31899  * Create a new TreePanel. - defaults to fit/scoll contents.
31900  * @param {String/Object} config A string to set only the panel's title, or a config object
31901  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31902  */
31903 Roo.TreePanel = function(config){
31904     var el = config.el;
31905     var tree = config.tree;
31906     delete config.tree; 
31907     delete config.el; // hopefull!
31908     
31909     // wrapper for IE7 strict & safari scroll issue
31910     
31911     var treeEl = el.createChild();
31912     config.resizeEl = treeEl;
31913     
31914     
31915     
31916     Roo.TreePanel.superclass.constructor.call(this, el, config);
31917  
31918  
31919     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31920     //console.log(tree);
31921     this.on('activate', function()
31922     {
31923         if (this.tree.rendered) {
31924             return;
31925         }
31926         //console.log('render tree');
31927         this.tree.render();
31928     });
31929     // this should not be needed.. - it's actually the 'el' that resizes?
31930     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31931     
31932     //this.on('resize',  function (cp, w, h) {
31933     //        this.tree.innerCt.setWidth(w);
31934     //        this.tree.innerCt.setHeight(h);
31935     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31936     //});
31937
31938         
31939     
31940 };
31941
31942 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31943     fitToFrame : true,
31944     autoScroll : true
31945 });
31946
31947
31948
31949
31950
31951
31952
31953
31954
31955
31956
31957 /*
31958  * Based on:
31959  * Ext JS Library 1.1.1
31960  * Copyright(c) 2006-2007, Ext JS, LLC.
31961  *
31962  * Originally Released Under LGPL - original licence link has changed is not relivant.
31963  *
31964  * Fork - LGPL
31965  * <script type="text/javascript">
31966  */
31967  
31968
31969 /**
31970  * @class Roo.ReaderLayout
31971  * @extends Roo.BorderLayout
31972  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31973  * center region containing two nested regions (a top one for a list view and one for item preview below),
31974  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31975  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31976  * expedites the setup of the overall layout and regions for this common application style.
31977  * Example:
31978  <pre><code>
31979 var reader = new Roo.ReaderLayout();
31980 var CP = Roo.ContentPanel;  // shortcut for adding
31981
31982 reader.beginUpdate();
31983 reader.add("north", new CP("north", "North"));
31984 reader.add("west", new CP("west", {title: "West"}));
31985 reader.add("east", new CP("east", {title: "East"}));
31986
31987 reader.regions.listView.add(new CP("listView", "List"));
31988 reader.regions.preview.add(new CP("preview", "Preview"));
31989 reader.endUpdate();
31990 </code></pre>
31991 * @constructor
31992 * Create a new ReaderLayout
31993 * @param {Object} config Configuration options
31994 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31995 * document.body if omitted)
31996 */
31997 Roo.ReaderLayout = function(config, renderTo){
31998     var c = config || {size:{}};
31999     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
32000         north: c.north !== false ? Roo.apply({
32001             split:false,
32002             initialSize: 32,
32003             titlebar: false
32004         }, c.north) : false,
32005         west: c.west !== false ? Roo.apply({
32006             split:true,
32007             initialSize: 200,
32008             minSize: 175,
32009             maxSize: 400,
32010             titlebar: true,
32011             collapsible: true,
32012             animate: true,
32013             margins:{left:5,right:0,bottom:5,top:5},
32014             cmargins:{left:5,right:5,bottom:5,top:5}
32015         }, c.west) : false,
32016         east: c.east !== false ? Roo.apply({
32017             split:true,
32018             initialSize: 200,
32019             minSize: 175,
32020             maxSize: 400,
32021             titlebar: true,
32022             collapsible: true,
32023             animate: true,
32024             margins:{left:0,right:5,bottom:5,top:5},
32025             cmargins:{left:5,right:5,bottom:5,top:5}
32026         }, c.east) : false,
32027         center: Roo.apply({
32028             tabPosition: 'top',
32029             autoScroll:false,
32030             closeOnTab: true,
32031             titlebar:false,
32032             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32033         }, c.center)
32034     });
32035
32036     this.el.addClass('x-reader');
32037
32038     this.beginUpdate();
32039
32040     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32041         south: c.preview !== false ? Roo.apply({
32042             split:true,
32043             initialSize: 200,
32044             minSize: 100,
32045             autoScroll:true,
32046             collapsible:true,
32047             titlebar: true,
32048             cmargins:{top:5,left:0, right:0, bottom:0}
32049         }, c.preview) : false,
32050         center: Roo.apply({
32051             autoScroll:false,
32052             titlebar:false,
32053             minHeight:200
32054         }, c.listView)
32055     });
32056     this.add('center', new Roo.NestedLayoutPanel(inner,
32057             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32058
32059     this.endUpdate();
32060
32061     this.regions.preview = inner.getRegion('south');
32062     this.regions.listView = inner.getRegion('center');
32063 };
32064
32065 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32066  * Based on:
32067  * Ext JS Library 1.1.1
32068  * Copyright(c) 2006-2007, Ext JS, LLC.
32069  *
32070  * Originally Released Under LGPL - original licence link has changed is not relivant.
32071  *
32072  * Fork - LGPL
32073  * <script type="text/javascript">
32074  */
32075  
32076 /**
32077  * @class Roo.grid.Grid
32078  * @extends Roo.util.Observable
32079  * This class represents the primary interface of a component based grid control.
32080  * <br><br>Usage:<pre><code>
32081  var grid = new Roo.grid.Grid("my-container-id", {
32082      ds: myDataStore,
32083      cm: myColModel,
32084      selModel: mySelectionModel,
32085      autoSizeColumns: true,
32086      monitorWindowResize: false,
32087      trackMouseOver: true
32088  });
32089  // set any options
32090  grid.render();
32091  * </code></pre>
32092  * <b>Common Problems:</b><br/>
32093  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32094  * element will correct this<br/>
32095  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32096  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32097  * are unpredictable.<br/>
32098  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32099  * grid to calculate dimensions/offsets.<br/>
32100   * @constructor
32101  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32102  * The container MUST have some type of size defined for the grid to fill. The container will be
32103  * automatically set to position relative if it isn't already.
32104  * @param {Object} config A config object that sets properties on this grid.
32105  */
32106 Roo.grid.Grid = function(container, config){
32107         // initialize the container
32108         this.container = Roo.get(container);
32109         this.container.update("");
32110         this.container.setStyle("overflow", "hidden");
32111     this.container.addClass('x-grid-container');
32112
32113     this.id = this.container.id;
32114
32115     Roo.apply(this, config);
32116     // check and correct shorthanded configs
32117     if(this.ds){
32118         this.dataSource = this.ds;
32119         delete this.ds;
32120     }
32121     if(this.cm){
32122         this.colModel = this.cm;
32123         delete this.cm;
32124     }
32125     if(this.sm){
32126         this.selModel = this.sm;
32127         delete this.sm;
32128     }
32129
32130     if (this.selModel) {
32131         this.selModel = Roo.factory(this.selModel, Roo.grid);
32132         this.sm = this.selModel;
32133         this.sm.xmodule = this.xmodule || false;
32134     }
32135     if (typeof(this.colModel.config) == 'undefined') {
32136         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32137         this.cm = this.colModel;
32138         this.cm.xmodule = this.xmodule || false;
32139     }
32140     if (this.dataSource) {
32141         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32142         this.ds = this.dataSource;
32143         this.ds.xmodule = this.xmodule || false;
32144          
32145     }
32146     
32147     
32148     
32149     if(this.width){
32150         this.container.setWidth(this.width);
32151     }
32152
32153     if(this.height){
32154         this.container.setHeight(this.height);
32155     }
32156     /** @private */
32157         this.addEvents({
32158         // raw events
32159         /**
32160          * @event click
32161          * The raw click event for the entire grid.
32162          * @param {Roo.EventObject} e
32163          */
32164         "click" : true,
32165         /**
32166          * @event dblclick
32167          * The raw dblclick event for the entire grid.
32168          * @param {Roo.EventObject} e
32169          */
32170         "dblclick" : true,
32171         /**
32172          * @event contextmenu
32173          * The raw contextmenu event for the entire grid.
32174          * @param {Roo.EventObject} e
32175          */
32176         "contextmenu" : true,
32177         /**
32178          * @event mousedown
32179          * The raw mousedown event for the entire grid.
32180          * @param {Roo.EventObject} e
32181          */
32182         "mousedown" : true,
32183         /**
32184          * @event mouseup
32185          * The raw mouseup event for the entire grid.
32186          * @param {Roo.EventObject} e
32187          */
32188         "mouseup" : true,
32189         /**
32190          * @event mouseover
32191          * The raw mouseover event for the entire grid.
32192          * @param {Roo.EventObject} e
32193          */
32194         "mouseover" : true,
32195         /**
32196          * @event mouseout
32197          * The raw mouseout event for the entire grid.
32198          * @param {Roo.EventObject} e
32199          */
32200         "mouseout" : true,
32201         /**
32202          * @event keypress
32203          * The raw keypress event for the entire grid.
32204          * @param {Roo.EventObject} e
32205          */
32206         "keypress" : true,
32207         /**
32208          * @event keydown
32209          * The raw keydown event for the entire grid.
32210          * @param {Roo.EventObject} e
32211          */
32212         "keydown" : true,
32213
32214         // custom events
32215
32216         /**
32217          * @event cellclick
32218          * Fires when a cell is clicked
32219          * @param {Grid} this
32220          * @param {Number} rowIndex
32221          * @param {Number} columnIndex
32222          * @param {Roo.EventObject} e
32223          */
32224         "cellclick" : true,
32225         /**
32226          * @event celldblclick
32227          * Fires when a cell is double clicked
32228          * @param {Grid} this
32229          * @param {Number} rowIndex
32230          * @param {Number} columnIndex
32231          * @param {Roo.EventObject} e
32232          */
32233         "celldblclick" : true,
32234         /**
32235          * @event rowclick
32236          * Fires when a row is clicked
32237          * @param {Grid} this
32238          * @param {Number} rowIndex
32239          * @param {Roo.EventObject} e
32240          */
32241         "rowclick" : true,
32242         /**
32243          * @event rowdblclick
32244          * Fires when a row is double clicked
32245          * @param {Grid} this
32246          * @param {Number} rowIndex
32247          * @param {Roo.EventObject} e
32248          */
32249         "rowdblclick" : true,
32250         /**
32251          * @event headerclick
32252          * Fires when a header is clicked
32253          * @param {Grid} this
32254          * @param {Number} columnIndex
32255          * @param {Roo.EventObject} e
32256          */
32257         "headerclick" : true,
32258         /**
32259          * @event headerdblclick
32260          * Fires when a header cell is double clicked
32261          * @param {Grid} this
32262          * @param {Number} columnIndex
32263          * @param {Roo.EventObject} e
32264          */
32265         "headerdblclick" : true,
32266         /**
32267          * @event rowcontextmenu
32268          * Fires when a row is right clicked
32269          * @param {Grid} this
32270          * @param {Number} rowIndex
32271          * @param {Roo.EventObject} e
32272          */
32273         "rowcontextmenu" : true,
32274         /**
32275          * @event cellcontextmenu
32276          * Fires when a cell is right clicked
32277          * @param {Grid} this
32278          * @param {Number} rowIndex
32279          * @param {Number} cellIndex
32280          * @param {Roo.EventObject} e
32281          */
32282          "cellcontextmenu" : true,
32283         /**
32284          * @event headercontextmenu
32285          * Fires when a header is right clicked
32286          * @param {Grid} this
32287          * @param {Number} columnIndex
32288          * @param {Roo.EventObject} e
32289          */
32290         "headercontextmenu" : true,
32291         /**
32292          * @event bodyscroll
32293          * Fires when the body element is scrolled
32294          * @param {Number} scrollLeft
32295          * @param {Number} scrollTop
32296          */
32297         "bodyscroll" : true,
32298         /**
32299          * @event columnresize
32300          * Fires when the user resizes a column
32301          * @param {Number} columnIndex
32302          * @param {Number} newSize
32303          */
32304         "columnresize" : true,
32305         /**
32306          * @event columnmove
32307          * Fires when the user moves a column
32308          * @param {Number} oldIndex
32309          * @param {Number} newIndex
32310          */
32311         "columnmove" : true,
32312         /**
32313          * @event startdrag
32314          * Fires when row(s) start being dragged
32315          * @param {Grid} this
32316          * @param {Roo.GridDD} dd The drag drop object
32317          * @param {event} e The raw browser event
32318          */
32319         "startdrag" : true,
32320         /**
32321          * @event enddrag
32322          * Fires when a drag operation is complete
32323          * @param {Grid} this
32324          * @param {Roo.GridDD} dd The drag drop object
32325          * @param {event} e The raw browser event
32326          */
32327         "enddrag" : true,
32328         /**
32329          * @event dragdrop
32330          * Fires when dragged row(s) are dropped on a valid DD target
32331          * @param {Grid} this
32332          * @param {Roo.GridDD} dd The drag drop object
32333          * @param {String} targetId The target drag drop object
32334          * @param {event} e The raw browser event
32335          */
32336         "dragdrop" : true,
32337         /**
32338          * @event dragover
32339          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32340          * @param {Grid} this
32341          * @param {Roo.GridDD} dd The drag drop object
32342          * @param {String} targetId The target drag drop object
32343          * @param {event} e The raw browser event
32344          */
32345         "dragover" : true,
32346         /**
32347          * @event dragenter
32348          *  Fires when the dragged row(s) first cross another DD target while being dragged
32349          * @param {Grid} this
32350          * @param {Roo.GridDD} dd The drag drop object
32351          * @param {String} targetId The target drag drop object
32352          * @param {event} e The raw browser event
32353          */
32354         "dragenter" : true,
32355         /**
32356          * @event dragout
32357          * Fires when the dragged row(s) leave another DD target while being dragged
32358          * @param {Grid} this
32359          * @param {Roo.GridDD} dd The drag drop object
32360          * @param {String} targetId The target drag drop object
32361          * @param {event} e The raw browser event
32362          */
32363         "dragout" : true,
32364         /**
32365          * @event rowclass
32366          * Fires when a row is rendered, so you can change add a style to it.
32367          * @param {GridView} gridview   The grid view
32368          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32369          */
32370         'rowclass' : true,
32371
32372         /**
32373          * @event render
32374          * Fires when the grid is rendered
32375          * @param {Grid} grid
32376          */
32377         'render' : true
32378     });
32379
32380     Roo.grid.Grid.superclass.constructor.call(this);
32381 };
32382 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32383     
32384     /**
32385      * @cfg {String} ddGroup - drag drop group.
32386      */
32387
32388     /**
32389      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32390      */
32391     minColumnWidth : 25,
32392
32393     /**
32394      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32395      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32396      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32397      */
32398     autoSizeColumns : false,
32399
32400     /**
32401      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32402      */
32403     autoSizeHeaders : true,
32404
32405     /**
32406      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32407      */
32408     monitorWindowResize : true,
32409
32410     /**
32411      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32412      * rows measured to get a columns size. Default is 0 (all rows).
32413      */
32414     maxRowsToMeasure : 0,
32415
32416     /**
32417      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32418      */
32419     trackMouseOver : true,
32420
32421     /**
32422     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32423     */
32424     
32425     /**
32426     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32427     */
32428     enableDragDrop : false,
32429     
32430     /**
32431     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32432     */
32433     enableColumnMove : true,
32434     
32435     /**
32436     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32437     */
32438     enableColumnHide : true,
32439     
32440     /**
32441     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32442     */
32443     enableRowHeightSync : false,
32444     
32445     /**
32446     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32447     */
32448     stripeRows : true,
32449     
32450     /**
32451     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32452     */
32453     autoHeight : false,
32454
32455     /**
32456      * @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.
32457      */
32458     autoExpandColumn : false,
32459
32460     /**
32461     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32462     * Default is 50.
32463     */
32464     autoExpandMin : 50,
32465
32466     /**
32467     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32468     */
32469     autoExpandMax : 1000,
32470
32471     /**
32472     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32473     */
32474     view : null,
32475
32476     /**
32477     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32478     */
32479     loadMask : false,
32480     /**
32481     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32482     */
32483     dropTarget: false,
32484     
32485    
32486     
32487     // private
32488     rendered : false,
32489
32490     /**
32491     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32492     * of a fixed width. Default is false.
32493     */
32494     /**
32495     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32496     */
32497     /**
32498      * Called once after all setup has been completed and the grid is ready to be rendered.
32499      * @return {Roo.grid.Grid} this
32500      */
32501     render : function()
32502     {
32503         var c = this.container;
32504         // try to detect autoHeight/width mode
32505         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32506             this.autoHeight = true;
32507         }
32508         var view = this.getView();
32509         view.init(this);
32510
32511         c.on("click", this.onClick, this);
32512         c.on("dblclick", this.onDblClick, this);
32513         c.on("contextmenu", this.onContextMenu, this);
32514         c.on("keydown", this.onKeyDown, this);
32515         if (Roo.isTouch) {
32516             c.on("touchstart", this.onTouchStart, this);
32517         }
32518
32519         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32520
32521         this.getSelectionModel().init(this);
32522
32523         view.render();
32524
32525         if(this.loadMask){
32526             this.loadMask = new Roo.LoadMask(this.container,
32527                     Roo.apply({store:this.dataSource}, this.loadMask));
32528         }
32529         
32530         
32531         if (this.toolbar && this.toolbar.xtype) {
32532             this.toolbar.container = this.getView().getHeaderPanel(true);
32533             this.toolbar = new Roo.Toolbar(this.toolbar);
32534         }
32535         if (this.footer && this.footer.xtype) {
32536             this.footer.dataSource = this.getDataSource();
32537             this.footer.container = this.getView().getFooterPanel(true);
32538             this.footer = Roo.factory(this.footer, Roo);
32539         }
32540         if (this.dropTarget && this.dropTarget.xtype) {
32541             delete this.dropTarget.xtype;
32542             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32543         }
32544         
32545         
32546         this.rendered = true;
32547         this.fireEvent('render', this);
32548         return this;
32549     },
32550
32551     /**
32552      * Reconfigures the grid to use a different Store and Column Model.
32553      * The View will be bound to the new objects and refreshed.
32554      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32555      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32556      */
32557     reconfigure : function(dataSource, colModel){
32558         if(this.loadMask){
32559             this.loadMask.destroy();
32560             this.loadMask = new Roo.LoadMask(this.container,
32561                     Roo.apply({store:dataSource}, this.loadMask));
32562         }
32563         this.view.bind(dataSource, colModel);
32564         this.dataSource = dataSource;
32565         this.colModel = colModel;
32566         this.view.refresh(true);
32567     },
32568     /**
32569      * addColumns
32570      * Add's a column, default at the end..
32571      
32572      * @param {int} position to add (default end)
32573      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32574      */
32575     addColumns : function(pos, ar)
32576     {
32577         
32578         for (var i =0;i< ar.length;i++) {
32579             var cfg = ar[i];
32580             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32581             this.cm.lookup[cfg.id] = cfg;
32582         }
32583         
32584         
32585         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32586             pos = this.cm.config.length; //this.cm.config.push(cfg);
32587         } 
32588         pos = Math.max(0,pos);
32589         ar.unshift(0);
32590         ar.unshift(pos);
32591         this.cm.config.splice.apply(this.cm.config, ar);
32592         
32593         
32594         
32595         this.view.generateRules(this.cm);
32596         this.view.refresh(true);
32597         
32598     },
32599     
32600     
32601     
32602     
32603     // private
32604     onKeyDown : function(e){
32605         this.fireEvent("keydown", e);
32606     },
32607
32608     /**
32609      * Destroy this grid.
32610      * @param {Boolean} removeEl True to remove the element
32611      */
32612     destroy : function(removeEl, keepListeners){
32613         if(this.loadMask){
32614             this.loadMask.destroy();
32615         }
32616         var c = this.container;
32617         c.removeAllListeners();
32618         this.view.destroy();
32619         this.colModel.purgeListeners();
32620         if(!keepListeners){
32621             this.purgeListeners();
32622         }
32623         c.update("");
32624         if(removeEl === true){
32625             c.remove();
32626         }
32627     },
32628
32629     // private
32630     processEvent : function(name, e){
32631         // does this fire select???
32632         //Roo.log('grid:processEvent '  + name);
32633         
32634         if (name != 'touchstart' ) {
32635             this.fireEvent(name, e);    
32636         }
32637         
32638         var t = e.getTarget();
32639         var v = this.view;
32640         var header = v.findHeaderIndex(t);
32641         if(header !== false){
32642             var ename = name == 'touchstart' ? 'click' : name;
32643              
32644             this.fireEvent("header" + ename, this, header, e);
32645         }else{
32646             var row = v.findRowIndex(t);
32647             var cell = v.findCellIndex(t);
32648             if (name == 'touchstart') {
32649                 // first touch is always a click.
32650                 // hopefull this happens after selection is updated.?
32651                 name = false;
32652                 
32653                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32654                     var cs = this.selModel.getSelectedCell();
32655                     if (row == cs[0] && cell == cs[1]){
32656                         name = 'dblclick';
32657                     }
32658                 }
32659                 if (typeof(this.selModel.getSelections) != 'undefined') {
32660                     var cs = this.selModel.getSelections();
32661                     var ds = this.dataSource;
32662                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32663                         name = 'dblclick';
32664                     }
32665                 }
32666                 if (!name) {
32667                     return;
32668                 }
32669             }
32670             
32671             
32672             if(row !== false){
32673                 this.fireEvent("row" + name, this, row, e);
32674                 if(cell !== false){
32675                     this.fireEvent("cell" + name, this, row, cell, e);
32676                 }
32677             }
32678         }
32679     },
32680
32681     // private
32682     onClick : function(e){
32683         this.processEvent("click", e);
32684     },
32685    // private
32686     onTouchStart : function(e){
32687         this.processEvent("touchstart", e);
32688     },
32689
32690     // private
32691     onContextMenu : function(e, t){
32692         this.processEvent("contextmenu", e);
32693     },
32694
32695     // private
32696     onDblClick : function(e){
32697         this.processEvent("dblclick", e);
32698     },
32699
32700     // private
32701     walkCells : function(row, col, step, fn, scope){
32702         var cm = this.colModel, clen = cm.getColumnCount();
32703         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32704         if(step < 0){
32705             if(col < 0){
32706                 row--;
32707                 first = false;
32708             }
32709             while(row >= 0){
32710                 if(!first){
32711                     col = clen-1;
32712                 }
32713                 first = false;
32714                 while(col >= 0){
32715                     if(fn.call(scope || this, row, col, cm) === true){
32716                         return [row, col];
32717                     }
32718                     col--;
32719                 }
32720                 row--;
32721             }
32722         } else {
32723             if(col >= clen){
32724                 row++;
32725                 first = false;
32726             }
32727             while(row < rlen){
32728                 if(!first){
32729                     col = 0;
32730                 }
32731                 first = false;
32732                 while(col < clen){
32733                     if(fn.call(scope || this, row, col, cm) === true){
32734                         return [row, col];
32735                     }
32736                     col++;
32737                 }
32738                 row++;
32739             }
32740         }
32741         return null;
32742     },
32743
32744     // private
32745     getSelections : function(){
32746         return this.selModel.getSelections();
32747     },
32748
32749     /**
32750      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32751      * but if manual update is required this method will initiate it.
32752      */
32753     autoSize : function(){
32754         if(this.rendered){
32755             this.view.layout();
32756             if(this.view.adjustForScroll){
32757                 this.view.adjustForScroll();
32758             }
32759         }
32760     },
32761
32762     /**
32763      * Returns the grid's underlying element.
32764      * @return {Element} The element
32765      */
32766     getGridEl : function(){
32767         return this.container;
32768     },
32769
32770     // private for compatibility, overridden by editor grid
32771     stopEditing : function(){},
32772
32773     /**
32774      * Returns the grid's SelectionModel.
32775      * @return {SelectionModel}
32776      */
32777     getSelectionModel : function(){
32778         if(!this.selModel){
32779             this.selModel = new Roo.grid.RowSelectionModel();
32780         }
32781         return this.selModel;
32782     },
32783
32784     /**
32785      * Returns the grid's DataSource.
32786      * @return {DataSource}
32787      */
32788     getDataSource : function(){
32789         return this.dataSource;
32790     },
32791
32792     /**
32793      * Returns the grid's ColumnModel.
32794      * @return {ColumnModel}
32795      */
32796     getColumnModel : function(){
32797         return this.colModel;
32798     },
32799
32800     /**
32801      * Returns the grid's GridView object.
32802      * @return {GridView}
32803      */
32804     getView : function(){
32805         if(!this.view){
32806             this.view = new Roo.grid.GridView(this.viewConfig);
32807         }
32808         return this.view;
32809     },
32810     /**
32811      * Called to get grid's drag proxy text, by default returns this.ddText.
32812      * @return {String}
32813      */
32814     getDragDropText : function(){
32815         var count = this.selModel.getCount();
32816         return String.format(this.ddText, count, count == 1 ? '' : 's');
32817     }
32818 });
32819 /**
32820  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32821  * %0 is replaced with the number of selected rows.
32822  * @type String
32823  */
32824 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32825  * Based on:
32826  * Ext JS Library 1.1.1
32827  * Copyright(c) 2006-2007, Ext JS, LLC.
32828  *
32829  * Originally Released Under LGPL - original licence link has changed is not relivant.
32830  *
32831  * Fork - LGPL
32832  * <script type="text/javascript">
32833  */
32834  
32835 Roo.grid.AbstractGridView = function(){
32836         this.grid = null;
32837         
32838         this.events = {
32839             "beforerowremoved" : true,
32840             "beforerowsinserted" : true,
32841             "beforerefresh" : true,
32842             "rowremoved" : true,
32843             "rowsinserted" : true,
32844             "rowupdated" : true,
32845             "refresh" : true
32846         };
32847     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32848 };
32849
32850 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32851     rowClass : "x-grid-row",
32852     cellClass : "x-grid-cell",
32853     tdClass : "x-grid-td",
32854     hdClass : "x-grid-hd",
32855     splitClass : "x-grid-hd-split",
32856     
32857     init: function(grid){
32858         this.grid = grid;
32859                 var cid = this.grid.getGridEl().id;
32860         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32861         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32862         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32863         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32864         },
32865         
32866     getColumnRenderers : function(){
32867         var renderers = [];
32868         var cm = this.grid.colModel;
32869         var colCount = cm.getColumnCount();
32870         for(var i = 0; i < colCount; i++){
32871             renderers[i] = cm.getRenderer(i);
32872         }
32873         return renderers;
32874     },
32875     
32876     getColumnIds : function(){
32877         var ids = [];
32878         var cm = this.grid.colModel;
32879         var colCount = cm.getColumnCount();
32880         for(var i = 0; i < colCount; i++){
32881             ids[i] = cm.getColumnId(i);
32882         }
32883         return ids;
32884     },
32885     
32886     getDataIndexes : function(){
32887         if(!this.indexMap){
32888             this.indexMap = this.buildIndexMap();
32889         }
32890         return this.indexMap.colToData;
32891     },
32892     
32893     getColumnIndexByDataIndex : function(dataIndex){
32894         if(!this.indexMap){
32895             this.indexMap = this.buildIndexMap();
32896         }
32897         return this.indexMap.dataToCol[dataIndex];
32898     },
32899     
32900     /**
32901      * Set a css style for a column dynamically. 
32902      * @param {Number} colIndex The index of the column
32903      * @param {String} name The css property name
32904      * @param {String} value The css value
32905      */
32906     setCSSStyle : function(colIndex, name, value){
32907         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32908         Roo.util.CSS.updateRule(selector, name, value);
32909     },
32910     
32911     generateRules : function(cm){
32912         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32913         Roo.util.CSS.removeStyleSheet(rulesId);
32914         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32915             var cid = cm.getColumnId(i);
32916             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32917                          this.tdSelector, cid, " {\n}\n",
32918                          this.hdSelector, cid, " {\n}\n",
32919                          this.splitSelector, cid, " {\n}\n");
32920         }
32921         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32922     }
32923 });/*
32924  * Based on:
32925  * Ext JS Library 1.1.1
32926  * Copyright(c) 2006-2007, Ext JS, LLC.
32927  *
32928  * Originally Released Under LGPL - original licence link has changed is not relivant.
32929  *
32930  * Fork - LGPL
32931  * <script type="text/javascript">
32932  */
32933
32934 // private
32935 // This is a support class used internally by the Grid components
32936 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32937     this.grid = grid;
32938     this.view = grid.getView();
32939     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32940     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32941     if(hd2){
32942         this.setHandleElId(Roo.id(hd));
32943         this.setOuterHandleElId(Roo.id(hd2));
32944     }
32945     this.scroll = false;
32946 };
32947 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32948     maxDragWidth: 120,
32949     getDragData : function(e){
32950         var t = Roo.lib.Event.getTarget(e);
32951         var h = this.view.findHeaderCell(t);
32952         if(h){
32953             return {ddel: h.firstChild, header:h};
32954         }
32955         return false;
32956     },
32957
32958     onInitDrag : function(e){
32959         this.view.headersDisabled = true;
32960         var clone = this.dragData.ddel.cloneNode(true);
32961         clone.id = Roo.id();
32962         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32963         this.proxy.update(clone);
32964         return true;
32965     },
32966
32967     afterValidDrop : function(){
32968         var v = this.view;
32969         setTimeout(function(){
32970             v.headersDisabled = false;
32971         }, 50);
32972     },
32973
32974     afterInvalidDrop : function(){
32975         var v = this.view;
32976         setTimeout(function(){
32977             v.headersDisabled = false;
32978         }, 50);
32979     }
32980 });
32981 /*
32982  * Based on:
32983  * Ext JS Library 1.1.1
32984  * Copyright(c) 2006-2007, Ext JS, LLC.
32985  *
32986  * Originally Released Under LGPL - original licence link has changed is not relivant.
32987  *
32988  * Fork - LGPL
32989  * <script type="text/javascript">
32990  */
32991 // private
32992 // This is a support class used internally by the Grid components
32993 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32994     this.grid = grid;
32995     this.view = grid.getView();
32996     // split the proxies so they don't interfere with mouse events
32997     this.proxyTop = Roo.DomHelper.append(document.body, {
32998         cls:"col-move-top", html:"&#160;"
32999     }, true);
33000     this.proxyBottom = Roo.DomHelper.append(document.body, {
33001         cls:"col-move-bottom", html:"&#160;"
33002     }, true);
33003     this.proxyTop.hide = this.proxyBottom.hide = function(){
33004         this.setLeftTop(-100,-100);
33005         this.setStyle("visibility", "hidden");
33006     };
33007     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33008     // temporarily disabled
33009     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33010     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33011 };
33012 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33013     proxyOffsets : [-4, -9],
33014     fly: Roo.Element.fly,
33015
33016     getTargetFromEvent : function(e){
33017         var t = Roo.lib.Event.getTarget(e);
33018         var cindex = this.view.findCellIndex(t);
33019         if(cindex !== false){
33020             return this.view.getHeaderCell(cindex);
33021         }
33022         return null;
33023     },
33024
33025     nextVisible : function(h){
33026         var v = this.view, cm = this.grid.colModel;
33027         h = h.nextSibling;
33028         while(h){
33029             if(!cm.isHidden(v.getCellIndex(h))){
33030                 return h;
33031             }
33032             h = h.nextSibling;
33033         }
33034         return null;
33035     },
33036
33037     prevVisible : function(h){
33038         var v = this.view, cm = this.grid.colModel;
33039         h = h.prevSibling;
33040         while(h){
33041             if(!cm.isHidden(v.getCellIndex(h))){
33042                 return h;
33043             }
33044             h = h.prevSibling;
33045         }
33046         return null;
33047     },
33048
33049     positionIndicator : function(h, n, e){
33050         var x = Roo.lib.Event.getPageX(e);
33051         var r = Roo.lib.Dom.getRegion(n.firstChild);
33052         var px, pt, py = r.top + this.proxyOffsets[1];
33053         if((r.right - x) <= (r.right-r.left)/2){
33054             px = r.right+this.view.borderWidth;
33055             pt = "after";
33056         }else{
33057             px = r.left;
33058             pt = "before";
33059         }
33060         var oldIndex = this.view.getCellIndex(h);
33061         var newIndex = this.view.getCellIndex(n);
33062
33063         if(this.grid.colModel.isFixed(newIndex)){
33064             return false;
33065         }
33066
33067         var locked = this.grid.colModel.isLocked(newIndex);
33068
33069         if(pt == "after"){
33070             newIndex++;
33071         }
33072         if(oldIndex < newIndex){
33073             newIndex--;
33074         }
33075         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33076             return false;
33077         }
33078         px +=  this.proxyOffsets[0];
33079         this.proxyTop.setLeftTop(px, py);
33080         this.proxyTop.show();
33081         if(!this.bottomOffset){
33082             this.bottomOffset = this.view.mainHd.getHeight();
33083         }
33084         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33085         this.proxyBottom.show();
33086         return pt;
33087     },
33088
33089     onNodeEnter : function(n, dd, e, data){
33090         if(data.header != n){
33091             this.positionIndicator(data.header, n, e);
33092         }
33093     },
33094
33095     onNodeOver : function(n, dd, e, data){
33096         var result = false;
33097         if(data.header != n){
33098             result = this.positionIndicator(data.header, n, e);
33099         }
33100         if(!result){
33101             this.proxyTop.hide();
33102             this.proxyBottom.hide();
33103         }
33104         return result ? this.dropAllowed : this.dropNotAllowed;
33105     },
33106
33107     onNodeOut : function(n, dd, e, data){
33108         this.proxyTop.hide();
33109         this.proxyBottom.hide();
33110     },
33111
33112     onNodeDrop : function(n, dd, e, data){
33113         var h = data.header;
33114         if(h != n){
33115             var cm = this.grid.colModel;
33116             var x = Roo.lib.Event.getPageX(e);
33117             var r = Roo.lib.Dom.getRegion(n.firstChild);
33118             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33119             var oldIndex = this.view.getCellIndex(h);
33120             var newIndex = this.view.getCellIndex(n);
33121             var locked = cm.isLocked(newIndex);
33122             if(pt == "after"){
33123                 newIndex++;
33124             }
33125             if(oldIndex < newIndex){
33126                 newIndex--;
33127             }
33128             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33129                 return false;
33130             }
33131             cm.setLocked(oldIndex, locked, true);
33132             cm.moveColumn(oldIndex, newIndex);
33133             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33134             return true;
33135         }
33136         return false;
33137     }
33138 });
33139 /*
33140  * Based on:
33141  * Ext JS Library 1.1.1
33142  * Copyright(c) 2006-2007, Ext JS, LLC.
33143  *
33144  * Originally Released Under LGPL - original licence link has changed is not relivant.
33145  *
33146  * Fork - LGPL
33147  * <script type="text/javascript">
33148  */
33149   
33150 /**
33151  * @class Roo.grid.GridView
33152  * @extends Roo.util.Observable
33153  *
33154  * @constructor
33155  * @param {Object} config
33156  */
33157 Roo.grid.GridView = function(config){
33158     Roo.grid.GridView.superclass.constructor.call(this);
33159     this.el = null;
33160
33161     Roo.apply(this, config);
33162 };
33163
33164 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33165
33166     unselectable :  'unselectable="on"',
33167     unselectableCls :  'x-unselectable',
33168     
33169     
33170     rowClass : "x-grid-row",
33171
33172     cellClass : "x-grid-col",
33173
33174     tdClass : "x-grid-td",
33175
33176     hdClass : "x-grid-hd",
33177
33178     splitClass : "x-grid-split",
33179
33180     sortClasses : ["sort-asc", "sort-desc"],
33181
33182     enableMoveAnim : false,
33183
33184     hlColor: "C3DAF9",
33185
33186     dh : Roo.DomHelper,
33187
33188     fly : Roo.Element.fly,
33189
33190     css : Roo.util.CSS,
33191
33192     borderWidth: 1,
33193
33194     splitOffset: 3,
33195
33196     scrollIncrement : 22,
33197
33198     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33199
33200     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33201
33202     bind : function(ds, cm){
33203         if(this.ds){
33204             this.ds.un("load", this.onLoad, this);
33205             this.ds.un("datachanged", this.onDataChange, this);
33206             this.ds.un("add", this.onAdd, this);
33207             this.ds.un("remove", this.onRemove, this);
33208             this.ds.un("update", this.onUpdate, this);
33209             this.ds.un("clear", this.onClear, this);
33210         }
33211         if(ds){
33212             ds.on("load", this.onLoad, this);
33213             ds.on("datachanged", this.onDataChange, this);
33214             ds.on("add", this.onAdd, this);
33215             ds.on("remove", this.onRemove, this);
33216             ds.on("update", this.onUpdate, this);
33217             ds.on("clear", this.onClear, this);
33218         }
33219         this.ds = ds;
33220
33221         if(this.cm){
33222             this.cm.un("widthchange", this.onColWidthChange, this);
33223             this.cm.un("headerchange", this.onHeaderChange, this);
33224             this.cm.un("hiddenchange", this.onHiddenChange, this);
33225             this.cm.un("columnmoved", this.onColumnMove, this);
33226             this.cm.un("columnlockchange", this.onColumnLock, this);
33227         }
33228         if(cm){
33229             this.generateRules(cm);
33230             cm.on("widthchange", this.onColWidthChange, this);
33231             cm.on("headerchange", this.onHeaderChange, this);
33232             cm.on("hiddenchange", this.onHiddenChange, this);
33233             cm.on("columnmoved", this.onColumnMove, this);
33234             cm.on("columnlockchange", this.onColumnLock, this);
33235         }
33236         this.cm = cm;
33237     },
33238
33239     init: function(grid){
33240         Roo.grid.GridView.superclass.init.call(this, grid);
33241
33242         this.bind(grid.dataSource, grid.colModel);
33243
33244         grid.on("headerclick", this.handleHeaderClick, this);
33245
33246         if(grid.trackMouseOver){
33247             grid.on("mouseover", this.onRowOver, this);
33248             grid.on("mouseout", this.onRowOut, this);
33249         }
33250         grid.cancelTextSelection = function(){};
33251         this.gridId = grid.id;
33252
33253         var tpls = this.templates || {};
33254
33255         if(!tpls.master){
33256             tpls.master = new Roo.Template(
33257                '<div class="x-grid" hidefocus="true">',
33258                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33259                   '<div class="x-grid-topbar"></div>',
33260                   '<div class="x-grid-scroller"><div></div></div>',
33261                   '<div class="x-grid-locked">',
33262                       '<div class="x-grid-header">{lockedHeader}</div>',
33263                       '<div class="x-grid-body">{lockedBody}</div>',
33264                   "</div>",
33265                   '<div class="x-grid-viewport">',
33266                       '<div class="x-grid-header">{header}</div>',
33267                       '<div class="x-grid-body">{body}</div>',
33268                   "</div>",
33269                   '<div class="x-grid-bottombar"></div>',
33270                  
33271                   '<div class="x-grid-resize-proxy">&#160;</div>',
33272                "</div>"
33273             );
33274             tpls.master.disableformats = true;
33275         }
33276
33277         if(!tpls.header){
33278             tpls.header = new Roo.Template(
33279                '<table border="0" cellspacing="0" cellpadding="0">',
33280                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33281                "</table>{splits}"
33282             );
33283             tpls.header.disableformats = true;
33284         }
33285         tpls.header.compile();
33286
33287         if(!tpls.hcell){
33288             tpls.hcell = new Roo.Template(
33289                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33290                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33291                 "</div></td>"
33292              );
33293              tpls.hcell.disableFormats = true;
33294         }
33295         tpls.hcell.compile();
33296
33297         if(!tpls.hsplit){
33298             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33299                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33300             tpls.hsplit.disableFormats = true;
33301         }
33302         tpls.hsplit.compile();
33303
33304         if(!tpls.body){
33305             tpls.body = new Roo.Template(
33306                '<table border="0" cellspacing="0" cellpadding="0">',
33307                "<tbody>{rows}</tbody>",
33308                "</table>"
33309             );
33310             tpls.body.disableFormats = true;
33311         }
33312         tpls.body.compile();
33313
33314         if(!tpls.row){
33315             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33316             tpls.row.disableFormats = true;
33317         }
33318         tpls.row.compile();
33319
33320         if(!tpls.cell){
33321             tpls.cell = new Roo.Template(
33322                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33323                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33324                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33325                 "</td>"
33326             );
33327             tpls.cell.disableFormats = true;
33328         }
33329         tpls.cell.compile();
33330
33331         this.templates = tpls;
33332     },
33333
33334     // remap these for backwards compat
33335     onColWidthChange : function(){
33336         this.updateColumns.apply(this, arguments);
33337     },
33338     onHeaderChange : function(){
33339         this.updateHeaders.apply(this, arguments);
33340     }, 
33341     onHiddenChange : function(){
33342         this.handleHiddenChange.apply(this, arguments);
33343     },
33344     onColumnMove : function(){
33345         this.handleColumnMove.apply(this, arguments);
33346     },
33347     onColumnLock : function(){
33348         this.handleLockChange.apply(this, arguments);
33349     },
33350
33351     onDataChange : function(){
33352         this.refresh();
33353         this.updateHeaderSortState();
33354     },
33355
33356     onClear : function(){
33357         this.refresh();
33358     },
33359
33360     onUpdate : function(ds, record){
33361         this.refreshRow(record);
33362     },
33363
33364     refreshRow : function(record){
33365         var ds = this.ds, index;
33366         if(typeof record == 'number'){
33367             index = record;
33368             record = ds.getAt(index);
33369         }else{
33370             index = ds.indexOf(record);
33371         }
33372         this.insertRows(ds, index, index, true);
33373         this.onRemove(ds, record, index+1, true);
33374         this.syncRowHeights(index, index);
33375         this.layout();
33376         this.fireEvent("rowupdated", this, index, record);
33377     },
33378
33379     onAdd : function(ds, records, index){
33380         this.insertRows(ds, index, index + (records.length-1));
33381     },
33382
33383     onRemove : function(ds, record, index, isUpdate){
33384         if(isUpdate !== true){
33385             this.fireEvent("beforerowremoved", this, index, record);
33386         }
33387         var bt = this.getBodyTable(), lt = this.getLockedTable();
33388         if(bt.rows[index]){
33389             bt.firstChild.removeChild(bt.rows[index]);
33390         }
33391         if(lt.rows[index]){
33392             lt.firstChild.removeChild(lt.rows[index]);
33393         }
33394         if(isUpdate !== true){
33395             this.stripeRows(index);
33396             this.syncRowHeights(index, index);
33397             this.layout();
33398             this.fireEvent("rowremoved", this, index, record);
33399         }
33400     },
33401
33402     onLoad : function(){
33403         this.scrollToTop();
33404     },
33405
33406     /**
33407      * Scrolls the grid to the top
33408      */
33409     scrollToTop : function(){
33410         if(this.scroller){
33411             this.scroller.dom.scrollTop = 0;
33412             this.syncScroll();
33413         }
33414     },
33415
33416     /**
33417      * Gets a panel in the header of the grid that can be used for toolbars etc.
33418      * After modifying the contents of this panel a call to grid.autoSize() may be
33419      * required to register any changes in size.
33420      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33421      * @return Roo.Element
33422      */
33423     getHeaderPanel : function(doShow){
33424         if(doShow){
33425             this.headerPanel.show();
33426         }
33427         return this.headerPanel;
33428     },
33429
33430     /**
33431      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33432      * After modifying the contents of this panel a call to grid.autoSize() may be
33433      * required to register any changes in size.
33434      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33435      * @return Roo.Element
33436      */
33437     getFooterPanel : function(doShow){
33438         if(doShow){
33439             this.footerPanel.show();
33440         }
33441         return this.footerPanel;
33442     },
33443
33444     initElements : function(){
33445         var E = Roo.Element;
33446         var el = this.grid.getGridEl().dom.firstChild;
33447         var cs = el.childNodes;
33448
33449         this.el = new E(el);
33450         
33451          this.focusEl = new E(el.firstChild);
33452         this.focusEl.swallowEvent("click", true);
33453         
33454         this.headerPanel = new E(cs[1]);
33455         this.headerPanel.enableDisplayMode("block");
33456
33457         this.scroller = new E(cs[2]);
33458         this.scrollSizer = new E(this.scroller.dom.firstChild);
33459
33460         this.lockedWrap = new E(cs[3]);
33461         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33462         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33463
33464         this.mainWrap = new E(cs[4]);
33465         this.mainHd = new E(this.mainWrap.dom.firstChild);
33466         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33467
33468         this.footerPanel = new E(cs[5]);
33469         this.footerPanel.enableDisplayMode("block");
33470
33471         this.resizeProxy = new E(cs[6]);
33472
33473         this.headerSelector = String.format(
33474            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33475            this.lockedHd.id, this.mainHd.id
33476         );
33477
33478         this.splitterSelector = String.format(
33479            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33480            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33481         );
33482     },
33483     idToCssName : function(s)
33484     {
33485         return s.replace(/[^a-z0-9]+/ig, '-');
33486     },
33487
33488     getHeaderCell : function(index){
33489         return Roo.DomQuery.select(this.headerSelector)[index];
33490     },
33491
33492     getHeaderCellMeasure : function(index){
33493         return this.getHeaderCell(index).firstChild;
33494     },
33495
33496     getHeaderCellText : function(index){
33497         return this.getHeaderCell(index).firstChild.firstChild;
33498     },
33499
33500     getLockedTable : function(){
33501         return this.lockedBody.dom.firstChild;
33502     },
33503
33504     getBodyTable : function(){
33505         return this.mainBody.dom.firstChild;
33506     },
33507
33508     getLockedRow : function(index){
33509         return this.getLockedTable().rows[index];
33510     },
33511
33512     getRow : function(index){
33513         return this.getBodyTable().rows[index];
33514     },
33515
33516     getRowComposite : function(index){
33517         if(!this.rowEl){
33518             this.rowEl = new Roo.CompositeElementLite();
33519         }
33520         var els = [], lrow, mrow;
33521         if(lrow = this.getLockedRow(index)){
33522             els.push(lrow);
33523         }
33524         if(mrow = this.getRow(index)){
33525             els.push(mrow);
33526         }
33527         this.rowEl.elements = els;
33528         return this.rowEl;
33529     },
33530     /**
33531      * Gets the 'td' of the cell
33532      * 
33533      * @param {Integer} rowIndex row to select
33534      * @param {Integer} colIndex column to select
33535      * 
33536      * @return {Object} 
33537      */
33538     getCell : function(rowIndex, colIndex){
33539         var locked = this.cm.getLockedCount();
33540         var source;
33541         if(colIndex < locked){
33542             source = this.lockedBody.dom.firstChild;
33543         }else{
33544             source = this.mainBody.dom.firstChild;
33545             colIndex -= locked;
33546         }
33547         return source.rows[rowIndex].childNodes[colIndex];
33548     },
33549
33550     getCellText : function(rowIndex, colIndex){
33551         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33552     },
33553
33554     getCellBox : function(cell){
33555         var b = this.fly(cell).getBox();
33556         if(Roo.isOpera){ // opera fails to report the Y
33557             b.y = cell.offsetTop + this.mainBody.getY();
33558         }
33559         return b;
33560     },
33561
33562     getCellIndex : function(cell){
33563         var id = String(cell.className).match(this.cellRE);
33564         if(id){
33565             return parseInt(id[1], 10);
33566         }
33567         return 0;
33568     },
33569
33570     findHeaderIndex : function(n){
33571         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33572         return r ? this.getCellIndex(r) : false;
33573     },
33574
33575     findHeaderCell : function(n){
33576         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33577         return r ? r : false;
33578     },
33579
33580     findRowIndex : function(n){
33581         if(!n){
33582             return false;
33583         }
33584         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33585         return r ? r.rowIndex : false;
33586     },
33587
33588     findCellIndex : function(node){
33589         var stop = this.el.dom;
33590         while(node && node != stop){
33591             if(this.findRE.test(node.className)){
33592                 return this.getCellIndex(node);
33593             }
33594             node = node.parentNode;
33595         }
33596         return false;
33597     },
33598
33599     getColumnId : function(index){
33600         return this.cm.getColumnId(index);
33601     },
33602
33603     getSplitters : function()
33604     {
33605         if(this.splitterSelector){
33606            return Roo.DomQuery.select(this.splitterSelector);
33607         }else{
33608             return null;
33609       }
33610     },
33611
33612     getSplitter : function(index){
33613         return this.getSplitters()[index];
33614     },
33615
33616     onRowOver : function(e, t){
33617         var row;
33618         if((row = this.findRowIndex(t)) !== false){
33619             this.getRowComposite(row).addClass("x-grid-row-over");
33620         }
33621     },
33622
33623     onRowOut : function(e, t){
33624         var row;
33625         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33626             this.getRowComposite(row).removeClass("x-grid-row-over");
33627         }
33628     },
33629
33630     renderHeaders : function(){
33631         var cm = this.cm;
33632         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33633         var cb = [], lb = [], sb = [], lsb = [], p = {};
33634         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33635             p.cellId = "x-grid-hd-0-" + i;
33636             p.splitId = "x-grid-csplit-0-" + i;
33637             p.id = cm.getColumnId(i);
33638             p.value = cm.getColumnHeader(i) || "";
33639             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33640             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33641             if(!cm.isLocked(i)){
33642                 cb[cb.length] = ct.apply(p);
33643                 sb[sb.length] = st.apply(p);
33644             }else{
33645                 lb[lb.length] = ct.apply(p);
33646                 lsb[lsb.length] = st.apply(p);
33647             }
33648         }
33649         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33650                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33651     },
33652
33653     updateHeaders : function(){
33654         var html = this.renderHeaders();
33655         this.lockedHd.update(html[0]);
33656         this.mainHd.update(html[1]);
33657     },
33658
33659     /**
33660      * Focuses the specified row.
33661      * @param {Number} row The row index
33662      */
33663     focusRow : function(row)
33664     {
33665         //Roo.log('GridView.focusRow');
33666         var x = this.scroller.dom.scrollLeft;
33667         this.focusCell(row, 0, false);
33668         this.scroller.dom.scrollLeft = x;
33669     },
33670
33671     /**
33672      * Focuses the specified cell.
33673      * @param {Number} row The row index
33674      * @param {Number} col The column index
33675      * @param {Boolean} hscroll false to disable horizontal scrolling
33676      */
33677     focusCell : function(row, col, hscroll)
33678     {
33679         //Roo.log('GridView.focusCell');
33680         var el = this.ensureVisible(row, col, hscroll);
33681         this.focusEl.alignTo(el, "tl-tl");
33682         if(Roo.isGecko){
33683             this.focusEl.focus();
33684         }else{
33685             this.focusEl.focus.defer(1, this.focusEl);
33686         }
33687     },
33688
33689     /**
33690      * Scrolls the specified cell into view
33691      * @param {Number} row The row index
33692      * @param {Number} col The column index
33693      * @param {Boolean} hscroll false to disable horizontal scrolling
33694      */
33695     ensureVisible : function(row, col, hscroll)
33696     {
33697         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33698         //return null; //disable for testing.
33699         if(typeof row != "number"){
33700             row = row.rowIndex;
33701         }
33702         if(row < 0 && row >= this.ds.getCount()){
33703             return  null;
33704         }
33705         col = (col !== undefined ? col : 0);
33706         var cm = this.grid.colModel;
33707         while(cm.isHidden(col)){
33708             col++;
33709         }
33710
33711         var el = this.getCell(row, col);
33712         if(!el){
33713             return null;
33714         }
33715         var c = this.scroller.dom;
33716
33717         var ctop = parseInt(el.offsetTop, 10);
33718         var cleft = parseInt(el.offsetLeft, 10);
33719         var cbot = ctop + el.offsetHeight;
33720         var cright = cleft + el.offsetWidth;
33721         
33722         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33723         var stop = parseInt(c.scrollTop, 10);
33724         var sleft = parseInt(c.scrollLeft, 10);
33725         var sbot = stop + ch;
33726         var sright = sleft + c.clientWidth;
33727         /*
33728         Roo.log('GridView.ensureVisible:' +
33729                 ' ctop:' + ctop +
33730                 ' c.clientHeight:' + c.clientHeight +
33731                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33732                 ' stop:' + stop +
33733                 ' cbot:' + cbot +
33734                 ' sbot:' + sbot +
33735                 ' ch:' + ch  
33736                 );
33737         */
33738         if(ctop < stop){
33739              c.scrollTop = ctop;
33740             //Roo.log("set scrolltop to ctop DISABLE?");
33741         }else if(cbot > sbot){
33742             //Roo.log("set scrolltop to cbot-ch");
33743             c.scrollTop = cbot-ch;
33744         }
33745         
33746         if(hscroll !== false){
33747             if(cleft < sleft){
33748                 c.scrollLeft = cleft;
33749             }else if(cright > sright){
33750                 c.scrollLeft = cright-c.clientWidth;
33751             }
33752         }
33753          
33754         return el;
33755     },
33756
33757     updateColumns : function(){
33758         this.grid.stopEditing();
33759         var cm = this.grid.colModel, colIds = this.getColumnIds();
33760         //var totalWidth = cm.getTotalWidth();
33761         var pos = 0;
33762         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33763             //if(cm.isHidden(i)) continue;
33764             var w = cm.getColumnWidth(i);
33765             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33766             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33767         }
33768         this.updateSplitters();
33769     },
33770
33771     generateRules : function(cm){
33772         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33773         Roo.util.CSS.removeStyleSheet(rulesId);
33774         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33775             var cid = cm.getColumnId(i);
33776             var align = '';
33777             if(cm.config[i].align){
33778                 align = 'text-align:'+cm.config[i].align+';';
33779             }
33780             var hidden = '';
33781             if(cm.isHidden(i)){
33782                 hidden = 'display:none;';
33783             }
33784             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33785             ruleBuf.push(
33786                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33787                     this.hdSelector, cid, " {\n", align, width, "}\n",
33788                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33789                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33790         }
33791         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33792     },
33793
33794     updateSplitters : function(){
33795         var cm = this.cm, s = this.getSplitters();
33796         if(s){ // splitters not created yet
33797             var pos = 0, locked = true;
33798             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33799                 if(cm.isHidden(i)) {
33800                     continue;
33801                 }
33802                 var w = cm.getColumnWidth(i); // make sure it's a number
33803                 if(!cm.isLocked(i) && locked){
33804                     pos = 0;
33805                     locked = false;
33806                 }
33807                 pos += w;
33808                 s[i].style.left = (pos-this.splitOffset) + "px";
33809             }
33810         }
33811     },
33812
33813     handleHiddenChange : function(colModel, colIndex, hidden){
33814         if(hidden){
33815             this.hideColumn(colIndex);
33816         }else{
33817             this.unhideColumn(colIndex);
33818         }
33819     },
33820
33821     hideColumn : function(colIndex){
33822         var cid = this.getColumnId(colIndex);
33823         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33824         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33825         if(Roo.isSafari){
33826             this.updateHeaders();
33827         }
33828         this.updateSplitters();
33829         this.layout();
33830     },
33831
33832     unhideColumn : function(colIndex){
33833         var cid = this.getColumnId(colIndex);
33834         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33835         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33836
33837         if(Roo.isSafari){
33838             this.updateHeaders();
33839         }
33840         this.updateSplitters();
33841         this.layout();
33842     },
33843
33844     insertRows : function(dm, firstRow, lastRow, isUpdate){
33845         if(firstRow == 0 && lastRow == dm.getCount()-1){
33846             this.refresh();
33847         }else{
33848             if(!isUpdate){
33849                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33850             }
33851             var s = this.getScrollState();
33852             var markup = this.renderRows(firstRow, lastRow);
33853             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33854             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33855             this.restoreScroll(s);
33856             if(!isUpdate){
33857                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33858                 this.syncRowHeights(firstRow, lastRow);
33859                 this.stripeRows(firstRow);
33860                 this.layout();
33861             }
33862         }
33863     },
33864
33865     bufferRows : function(markup, target, index){
33866         var before = null, trows = target.rows, tbody = target.tBodies[0];
33867         if(index < trows.length){
33868             before = trows[index];
33869         }
33870         var b = document.createElement("div");
33871         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33872         var rows = b.firstChild.rows;
33873         for(var i = 0, len = rows.length; i < len; i++){
33874             if(before){
33875                 tbody.insertBefore(rows[0], before);
33876             }else{
33877                 tbody.appendChild(rows[0]);
33878             }
33879         }
33880         b.innerHTML = "";
33881         b = null;
33882     },
33883
33884     deleteRows : function(dm, firstRow, lastRow){
33885         if(dm.getRowCount()<1){
33886             this.fireEvent("beforerefresh", this);
33887             this.mainBody.update("");
33888             this.lockedBody.update("");
33889             this.fireEvent("refresh", this);
33890         }else{
33891             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33892             var bt = this.getBodyTable();
33893             var tbody = bt.firstChild;
33894             var rows = bt.rows;
33895             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33896                 tbody.removeChild(rows[firstRow]);
33897             }
33898             this.stripeRows(firstRow);
33899             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33900         }
33901     },
33902
33903     updateRows : function(dataSource, firstRow, lastRow){
33904         var s = this.getScrollState();
33905         this.refresh();
33906         this.restoreScroll(s);
33907     },
33908
33909     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33910         if(!noRefresh){
33911            this.refresh();
33912         }
33913         this.updateHeaderSortState();
33914     },
33915
33916     getScrollState : function(){
33917         
33918         var sb = this.scroller.dom;
33919         return {left: sb.scrollLeft, top: sb.scrollTop};
33920     },
33921
33922     stripeRows : function(startRow){
33923         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33924             return;
33925         }
33926         startRow = startRow || 0;
33927         var rows = this.getBodyTable().rows;
33928         var lrows = this.getLockedTable().rows;
33929         var cls = ' x-grid-row-alt ';
33930         for(var i = startRow, len = rows.length; i < len; i++){
33931             var row = rows[i], lrow = lrows[i];
33932             var isAlt = ((i+1) % 2 == 0);
33933             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33934             if(isAlt == hasAlt){
33935                 continue;
33936             }
33937             if(isAlt){
33938                 row.className += " x-grid-row-alt";
33939             }else{
33940                 row.className = row.className.replace("x-grid-row-alt", "");
33941             }
33942             if(lrow){
33943                 lrow.className = row.className;
33944             }
33945         }
33946     },
33947
33948     restoreScroll : function(state){
33949         //Roo.log('GridView.restoreScroll');
33950         var sb = this.scroller.dom;
33951         sb.scrollLeft = state.left;
33952         sb.scrollTop = state.top;
33953         this.syncScroll();
33954     },
33955
33956     syncScroll : function(){
33957         //Roo.log('GridView.syncScroll');
33958         var sb = this.scroller.dom;
33959         var sh = this.mainHd.dom;
33960         var bs = this.mainBody.dom;
33961         var lv = this.lockedBody.dom;
33962         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33963         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33964     },
33965
33966     handleScroll : function(e){
33967         this.syncScroll();
33968         var sb = this.scroller.dom;
33969         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33970         e.stopEvent();
33971     },
33972
33973     handleWheel : function(e){
33974         var d = e.getWheelDelta();
33975         this.scroller.dom.scrollTop -= d*22;
33976         // set this here to prevent jumpy scrolling on large tables
33977         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33978         e.stopEvent();
33979     },
33980
33981     renderRows : function(startRow, endRow){
33982         // pull in all the crap needed to render rows
33983         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33984         var colCount = cm.getColumnCount();
33985
33986         if(ds.getCount() < 1){
33987             return ["", ""];
33988         }
33989
33990         // build a map for all the columns
33991         var cs = [];
33992         for(var i = 0; i < colCount; i++){
33993             var name = cm.getDataIndex(i);
33994             cs[i] = {
33995                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33996                 renderer : cm.getRenderer(i),
33997                 id : cm.getColumnId(i),
33998                 locked : cm.isLocked(i),
33999                 has_editor : cm.isCellEditable(i)
34000             };
34001         }
34002
34003         startRow = startRow || 0;
34004         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34005
34006         // records to render
34007         var rs = ds.getRange(startRow, endRow);
34008
34009         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34010     },
34011
34012     // As much as I hate to duplicate code, this was branched because FireFox really hates
34013     // [].join("") on strings. The performance difference was substantial enough to
34014     // branch this function
34015     doRender : Roo.isGecko ?
34016             function(cs, rs, ds, startRow, colCount, stripe){
34017                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34018                 // buffers
34019                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34020                 
34021                 var hasListener = this.grid.hasListener('rowclass');
34022                 var rowcfg = {};
34023                 for(var j = 0, len = rs.length; j < len; j++){
34024                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34025                     for(var i = 0; i < colCount; i++){
34026                         c = cs[i];
34027                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34028                         p.id = c.id;
34029                         p.css = p.attr = "";
34030                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34031                         if(p.value == undefined || p.value === "") {
34032                             p.value = "&#160;";
34033                         }
34034                         if(c.has_editor){
34035                             p.css += ' x-grid-editable-cell';
34036                         }
34037                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34038                             p.css +=  ' x-grid-dirty-cell';
34039                         }
34040                         var markup = ct.apply(p);
34041                         if(!c.locked){
34042                             cb+= markup;
34043                         }else{
34044                             lcb+= markup;
34045                         }
34046                     }
34047                     var alt = [];
34048                     if(stripe && ((rowIndex+1) % 2 == 0)){
34049                         alt.push("x-grid-row-alt")
34050                     }
34051                     if(r.dirty){
34052                         alt.push(  " x-grid-dirty-row");
34053                     }
34054                     rp.cells = lcb;
34055                     if(this.getRowClass){
34056                         alt.push(this.getRowClass(r, rowIndex));
34057                     }
34058                     if (hasListener) {
34059                         rowcfg = {
34060                              
34061                             record: r,
34062                             rowIndex : rowIndex,
34063                             rowClass : ''
34064                         };
34065                         this.grid.fireEvent('rowclass', this, rowcfg);
34066                         alt.push(rowcfg.rowClass);
34067                     }
34068                     rp.alt = alt.join(" ");
34069                     lbuf+= rt.apply(rp);
34070                     rp.cells = cb;
34071                     buf+=  rt.apply(rp);
34072                 }
34073                 return [lbuf, buf];
34074             } :
34075             function(cs, rs, ds, startRow, colCount, stripe){
34076                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34077                 // buffers
34078                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34079                 var hasListener = this.grid.hasListener('rowclass');
34080  
34081                 var rowcfg = {};
34082                 for(var j = 0, len = rs.length; j < len; j++){
34083                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34084                     for(var i = 0; i < colCount; i++){
34085                         c = cs[i];
34086                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34087                         p.id = c.id;
34088                         p.css = p.attr = "";
34089                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34090                         if(p.value == undefined || p.value === "") {
34091                             p.value = "&#160;";
34092                         }
34093                         //Roo.log(c);
34094                          if(c.has_editor){
34095                             p.css += ' x-grid-editable-cell';
34096                         }
34097                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34098                             p.css += ' x-grid-dirty-cell' 
34099                         }
34100                         
34101                         var markup = ct.apply(p);
34102                         if(!c.locked){
34103                             cb[cb.length] = markup;
34104                         }else{
34105                             lcb[lcb.length] = markup;
34106                         }
34107                     }
34108                     var alt = [];
34109                     if(stripe && ((rowIndex+1) % 2 == 0)){
34110                         alt.push( "x-grid-row-alt");
34111                     }
34112                     if(r.dirty){
34113                         alt.push(" x-grid-dirty-row");
34114                     }
34115                     rp.cells = lcb;
34116                     if(this.getRowClass){
34117                         alt.push( this.getRowClass(r, rowIndex));
34118                     }
34119                     if (hasListener) {
34120                         rowcfg = {
34121                              
34122                             record: r,
34123                             rowIndex : rowIndex,
34124                             rowClass : ''
34125                         };
34126                         this.grid.fireEvent('rowclass', this, rowcfg);
34127                         alt.push(rowcfg.rowClass);
34128                     }
34129                     
34130                     rp.alt = alt.join(" ");
34131                     rp.cells = lcb.join("");
34132                     lbuf[lbuf.length] = rt.apply(rp);
34133                     rp.cells = cb.join("");
34134                     buf[buf.length] =  rt.apply(rp);
34135                 }
34136                 return [lbuf.join(""), buf.join("")];
34137             },
34138
34139     renderBody : function(){
34140         var markup = this.renderRows();
34141         var bt = this.templates.body;
34142         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34143     },
34144
34145     /**
34146      * Refreshes the grid
34147      * @param {Boolean} headersToo
34148      */
34149     refresh : function(headersToo){
34150         this.fireEvent("beforerefresh", this);
34151         this.grid.stopEditing();
34152         var result = this.renderBody();
34153         this.lockedBody.update(result[0]);
34154         this.mainBody.update(result[1]);
34155         if(headersToo === true){
34156             this.updateHeaders();
34157             this.updateColumns();
34158             this.updateSplitters();
34159             this.updateHeaderSortState();
34160         }
34161         this.syncRowHeights();
34162         this.layout();
34163         this.fireEvent("refresh", this);
34164     },
34165
34166     handleColumnMove : function(cm, oldIndex, newIndex){
34167         this.indexMap = null;
34168         var s = this.getScrollState();
34169         this.refresh(true);
34170         this.restoreScroll(s);
34171         this.afterMove(newIndex);
34172     },
34173
34174     afterMove : function(colIndex){
34175         if(this.enableMoveAnim && Roo.enableFx){
34176             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34177         }
34178         // if multisort - fix sortOrder, and reload..
34179         if (this.grid.dataSource.multiSort) {
34180             // the we can call sort again..
34181             var dm = this.grid.dataSource;
34182             var cm = this.grid.colModel;
34183             var so = [];
34184             for(var i = 0; i < cm.config.length; i++ ) {
34185                 
34186                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34187                     continue; // dont' bother, it's not in sort list or being set.
34188                 }
34189                 
34190                 so.push(cm.config[i].dataIndex);
34191             };
34192             dm.sortOrder = so;
34193             dm.load(dm.lastOptions);
34194             
34195             
34196         }
34197         
34198     },
34199
34200     updateCell : function(dm, rowIndex, dataIndex){
34201         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34202         if(typeof colIndex == "undefined"){ // not present in grid
34203             return;
34204         }
34205         var cm = this.grid.colModel;
34206         var cell = this.getCell(rowIndex, colIndex);
34207         var cellText = this.getCellText(rowIndex, colIndex);
34208
34209         var p = {
34210             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34211             id : cm.getColumnId(colIndex),
34212             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34213         };
34214         var renderer = cm.getRenderer(colIndex);
34215         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34216         if(typeof val == "undefined" || val === "") {
34217             val = "&#160;";
34218         }
34219         cellText.innerHTML = val;
34220         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34221         this.syncRowHeights(rowIndex, rowIndex);
34222     },
34223
34224     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34225         var maxWidth = 0;
34226         if(this.grid.autoSizeHeaders){
34227             var h = this.getHeaderCellMeasure(colIndex);
34228             maxWidth = Math.max(maxWidth, h.scrollWidth);
34229         }
34230         var tb, index;
34231         if(this.cm.isLocked(colIndex)){
34232             tb = this.getLockedTable();
34233             index = colIndex;
34234         }else{
34235             tb = this.getBodyTable();
34236             index = colIndex - this.cm.getLockedCount();
34237         }
34238         if(tb && tb.rows){
34239             var rows = tb.rows;
34240             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34241             for(var i = 0; i < stopIndex; i++){
34242                 var cell = rows[i].childNodes[index].firstChild;
34243                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34244             }
34245         }
34246         return maxWidth + /*margin for error in IE*/ 5;
34247     },
34248     /**
34249      * Autofit a column to its content.
34250      * @param {Number} colIndex
34251      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34252      */
34253      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34254          if(this.cm.isHidden(colIndex)){
34255              return; // can't calc a hidden column
34256          }
34257         if(forceMinSize){
34258             var cid = this.cm.getColumnId(colIndex);
34259             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34260            if(this.grid.autoSizeHeaders){
34261                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34262            }
34263         }
34264         var newWidth = this.calcColumnWidth(colIndex);
34265         this.cm.setColumnWidth(colIndex,
34266             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34267         if(!suppressEvent){
34268             this.grid.fireEvent("columnresize", colIndex, newWidth);
34269         }
34270     },
34271
34272     /**
34273      * Autofits all columns to their content and then expands to fit any extra space in the grid
34274      */
34275      autoSizeColumns : function(){
34276         var cm = this.grid.colModel;
34277         var colCount = cm.getColumnCount();
34278         for(var i = 0; i < colCount; i++){
34279             this.autoSizeColumn(i, true, true);
34280         }
34281         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34282             this.fitColumns();
34283         }else{
34284             this.updateColumns();
34285             this.layout();
34286         }
34287     },
34288
34289     /**
34290      * Autofits all columns to the grid's width proportionate with their current size
34291      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34292      */
34293     fitColumns : function(reserveScrollSpace){
34294         var cm = this.grid.colModel;
34295         var colCount = cm.getColumnCount();
34296         var cols = [];
34297         var width = 0;
34298         var i, w;
34299         for (i = 0; i < colCount; i++){
34300             if(!cm.isHidden(i) && !cm.isFixed(i)){
34301                 w = cm.getColumnWidth(i);
34302                 cols.push(i);
34303                 cols.push(w);
34304                 width += w;
34305             }
34306         }
34307         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34308         if(reserveScrollSpace){
34309             avail -= 17;
34310         }
34311         var frac = (avail - cm.getTotalWidth())/width;
34312         while (cols.length){
34313             w = cols.pop();
34314             i = cols.pop();
34315             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34316         }
34317         this.updateColumns();
34318         this.layout();
34319     },
34320
34321     onRowSelect : function(rowIndex){
34322         var row = this.getRowComposite(rowIndex);
34323         row.addClass("x-grid-row-selected");
34324     },
34325
34326     onRowDeselect : function(rowIndex){
34327         var row = this.getRowComposite(rowIndex);
34328         row.removeClass("x-grid-row-selected");
34329     },
34330
34331     onCellSelect : function(row, col){
34332         var cell = this.getCell(row, col);
34333         if(cell){
34334             Roo.fly(cell).addClass("x-grid-cell-selected");
34335         }
34336     },
34337
34338     onCellDeselect : function(row, col){
34339         var cell = this.getCell(row, col);
34340         if(cell){
34341             Roo.fly(cell).removeClass("x-grid-cell-selected");
34342         }
34343     },
34344
34345     updateHeaderSortState : function(){
34346         
34347         // sort state can be single { field: xxx, direction : yyy}
34348         // or   { xxx=>ASC , yyy : DESC ..... }
34349         
34350         var mstate = {};
34351         if (!this.ds.multiSort) { 
34352             var state = this.ds.getSortState();
34353             if(!state){
34354                 return;
34355             }
34356             mstate[state.field] = state.direction;
34357             // FIXME... - this is not used here.. but might be elsewhere..
34358             this.sortState = state;
34359             
34360         } else {
34361             mstate = this.ds.sortToggle;
34362         }
34363         //remove existing sort classes..
34364         
34365         var sc = this.sortClasses;
34366         var hds = this.el.select(this.headerSelector).removeClass(sc);
34367         
34368         for(var f in mstate) {
34369         
34370             var sortColumn = this.cm.findColumnIndex(f);
34371             
34372             if(sortColumn != -1){
34373                 var sortDir = mstate[f];        
34374                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34375             }
34376         }
34377         
34378          
34379         
34380     },
34381
34382
34383     handleHeaderClick : function(g, index,e){
34384         
34385         Roo.log("header click");
34386         
34387         if (Roo.isTouch) {
34388             // touch events on header are handled by context
34389             this.handleHdCtx(g,index,e);
34390             return;
34391         }
34392         
34393         
34394         if(this.headersDisabled){
34395             return;
34396         }
34397         var dm = g.dataSource, cm = g.colModel;
34398         if(!cm.isSortable(index)){
34399             return;
34400         }
34401         g.stopEditing();
34402         
34403         if (dm.multiSort) {
34404             // update the sortOrder
34405             var so = [];
34406             for(var i = 0; i < cm.config.length; i++ ) {
34407                 
34408                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34409                     continue; // dont' bother, it's not in sort list or being set.
34410                 }
34411                 
34412                 so.push(cm.config[i].dataIndex);
34413             };
34414             dm.sortOrder = so;
34415         }
34416         
34417         
34418         dm.sort(cm.getDataIndex(index));
34419     },
34420
34421
34422     destroy : function(){
34423         if(this.colMenu){
34424             this.colMenu.removeAll();
34425             Roo.menu.MenuMgr.unregister(this.colMenu);
34426             this.colMenu.getEl().remove();
34427             delete this.colMenu;
34428         }
34429         if(this.hmenu){
34430             this.hmenu.removeAll();
34431             Roo.menu.MenuMgr.unregister(this.hmenu);
34432             this.hmenu.getEl().remove();
34433             delete this.hmenu;
34434         }
34435         if(this.grid.enableColumnMove){
34436             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34437             if(dds){
34438                 for(var dd in dds){
34439                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34440                         var elid = dds[dd].dragElId;
34441                         dds[dd].unreg();
34442                         Roo.get(elid).remove();
34443                     } else if(dds[dd].config.isTarget){
34444                         dds[dd].proxyTop.remove();
34445                         dds[dd].proxyBottom.remove();
34446                         dds[dd].unreg();
34447                     }
34448                     if(Roo.dd.DDM.locationCache[dd]){
34449                         delete Roo.dd.DDM.locationCache[dd];
34450                     }
34451                 }
34452                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34453             }
34454         }
34455         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34456         this.bind(null, null);
34457         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34458     },
34459
34460     handleLockChange : function(){
34461         this.refresh(true);
34462     },
34463
34464     onDenyColumnLock : function(){
34465
34466     },
34467
34468     onDenyColumnHide : function(){
34469
34470     },
34471
34472     handleHdMenuClick : function(item){
34473         var index = this.hdCtxIndex;
34474         var cm = this.cm, ds = this.ds;
34475         switch(item.id){
34476             case "asc":
34477                 ds.sort(cm.getDataIndex(index), "ASC");
34478                 break;
34479             case "desc":
34480                 ds.sort(cm.getDataIndex(index), "DESC");
34481                 break;
34482             case "lock":
34483                 var lc = cm.getLockedCount();
34484                 if(cm.getColumnCount(true) <= lc+1){
34485                     this.onDenyColumnLock();
34486                     return;
34487                 }
34488                 if(lc != index){
34489                     cm.setLocked(index, true, true);
34490                     cm.moveColumn(index, lc);
34491                     this.grid.fireEvent("columnmove", index, lc);
34492                 }else{
34493                     cm.setLocked(index, true);
34494                 }
34495             break;
34496             case "unlock":
34497                 var lc = cm.getLockedCount();
34498                 if((lc-1) != index){
34499                     cm.setLocked(index, false, true);
34500                     cm.moveColumn(index, lc-1);
34501                     this.grid.fireEvent("columnmove", index, lc-1);
34502                 }else{
34503                     cm.setLocked(index, false);
34504                 }
34505             break;
34506             case 'wider': // used to expand cols on touch..
34507             case 'narrow':
34508                 var cw = cm.getColumnWidth(index);
34509                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34510                 cw = Math.max(0, cw);
34511                 cw = Math.min(cw,4000);
34512                 cm.setColumnWidth(index, cw);
34513                 break;
34514                 
34515             default:
34516                 index = cm.getIndexById(item.id.substr(4));
34517                 if(index != -1){
34518                     if(item.checked && cm.getColumnCount(true) <= 1){
34519                         this.onDenyColumnHide();
34520                         return false;
34521                     }
34522                     cm.setHidden(index, item.checked);
34523                 }
34524         }
34525         return true;
34526     },
34527
34528     beforeColMenuShow : function(){
34529         var cm = this.cm,  colCount = cm.getColumnCount();
34530         this.colMenu.removeAll();
34531         for(var i = 0; i < colCount; i++){
34532             this.colMenu.add(new Roo.menu.CheckItem({
34533                 id: "col-"+cm.getColumnId(i),
34534                 text: cm.getColumnHeader(i),
34535                 checked: !cm.isHidden(i),
34536                 hideOnClick:false
34537             }));
34538         }
34539     },
34540
34541     handleHdCtx : function(g, index, e){
34542         e.stopEvent();
34543         var hd = this.getHeaderCell(index);
34544         this.hdCtxIndex = index;
34545         var ms = this.hmenu.items, cm = this.cm;
34546         ms.get("asc").setDisabled(!cm.isSortable(index));
34547         ms.get("desc").setDisabled(!cm.isSortable(index));
34548         if(this.grid.enableColLock !== false){
34549             ms.get("lock").setDisabled(cm.isLocked(index));
34550             ms.get("unlock").setDisabled(!cm.isLocked(index));
34551         }
34552         this.hmenu.show(hd, "tl-bl");
34553     },
34554
34555     handleHdOver : function(e){
34556         var hd = this.findHeaderCell(e.getTarget());
34557         if(hd && !this.headersDisabled){
34558             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34559                this.fly(hd).addClass("x-grid-hd-over");
34560             }
34561         }
34562     },
34563
34564     handleHdOut : function(e){
34565         var hd = this.findHeaderCell(e.getTarget());
34566         if(hd){
34567             this.fly(hd).removeClass("x-grid-hd-over");
34568         }
34569     },
34570
34571     handleSplitDblClick : function(e, t){
34572         var i = this.getCellIndex(t);
34573         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34574             this.autoSizeColumn(i, true);
34575             this.layout();
34576         }
34577     },
34578
34579     render : function(){
34580
34581         var cm = this.cm;
34582         var colCount = cm.getColumnCount();
34583
34584         if(this.grid.monitorWindowResize === true){
34585             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34586         }
34587         var header = this.renderHeaders();
34588         var body = this.templates.body.apply({rows:""});
34589         var html = this.templates.master.apply({
34590             lockedBody: body,
34591             body: body,
34592             lockedHeader: header[0],
34593             header: header[1]
34594         });
34595
34596         //this.updateColumns();
34597
34598         this.grid.getGridEl().dom.innerHTML = html;
34599
34600         this.initElements();
34601         
34602         // a kludge to fix the random scolling effect in webkit
34603         this.el.on("scroll", function() {
34604             this.el.dom.scrollTop=0; // hopefully not recursive..
34605         },this);
34606
34607         this.scroller.on("scroll", this.handleScroll, this);
34608         this.lockedBody.on("mousewheel", this.handleWheel, this);
34609         this.mainBody.on("mousewheel", this.handleWheel, this);
34610
34611         this.mainHd.on("mouseover", this.handleHdOver, this);
34612         this.mainHd.on("mouseout", this.handleHdOut, this);
34613         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34614                 {delegate: "."+this.splitClass});
34615
34616         this.lockedHd.on("mouseover", this.handleHdOver, this);
34617         this.lockedHd.on("mouseout", this.handleHdOut, this);
34618         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34619                 {delegate: "."+this.splitClass});
34620
34621         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34622             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34623         }
34624
34625         this.updateSplitters();
34626
34627         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34628             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34629             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34630         }
34631
34632         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34633             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34634             this.hmenu.add(
34635                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34636                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34637             );
34638             if(this.grid.enableColLock !== false){
34639                 this.hmenu.add('-',
34640                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34641                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34642                 );
34643             }
34644             if (Roo.isTouch) {
34645                  this.hmenu.add('-',
34646                     {id:"wider", text: this.columnsWiderText},
34647                     {id:"narrow", text: this.columnsNarrowText }
34648                 );
34649                 
34650                  
34651             }
34652             
34653             if(this.grid.enableColumnHide !== false){
34654
34655                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34656                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34657                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34658
34659                 this.hmenu.add('-',
34660                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34661                 );
34662             }
34663             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34664
34665             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34666         }
34667
34668         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34669             this.dd = new Roo.grid.GridDragZone(this.grid, {
34670                 ddGroup : this.grid.ddGroup || 'GridDD'
34671             });
34672             
34673         }
34674
34675         /*
34676         for(var i = 0; i < colCount; i++){
34677             if(cm.isHidden(i)){
34678                 this.hideColumn(i);
34679             }
34680             if(cm.config[i].align){
34681                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34682                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34683             }
34684         }*/
34685         
34686         this.updateHeaderSortState();
34687
34688         this.beforeInitialResize();
34689         this.layout(true);
34690
34691         // two part rendering gives faster view to the user
34692         this.renderPhase2.defer(1, this);
34693     },
34694
34695     renderPhase2 : function(){
34696         // render the rows now
34697         this.refresh();
34698         if(this.grid.autoSizeColumns){
34699             this.autoSizeColumns();
34700         }
34701     },
34702
34703     beforeInitialResize : function(){
34704
34705     },
34706
34707     onColumnSplitterMoved : function(i, w){
34708         this.userResized = true;
34709         var cm = this.grid.colModel;
34710         cm.setColumnWidth(i, w, true);
34711         var cid = cm.getColumnId(i);
34712         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34713         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34714         this.updateSplitters();
34715         this.layout();
34716         this.grid.fireEvent("columnresize", i, w);
34717     },
34718
34719     syncRowHeights : function(startIndex, endIndex){
34720         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34721             startIndex = startIndex || 0;
34722             var mrows = this.getBodyTable().rows;
34723             var lrows = this.getLockedTable().rows;
34724             var len = mrows.length-1;
34725             endIndex = Math.min(endIndex || len, len);
34726             for(var i = startIndex; i <= endIndex; i++){
34727                 var m = mrows[i], l = lrows[i];
34728                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34729                 m.style.height = l.style.height = h + "px";
34730             }
34731         }
34732     },
34733
34734     layout : function(initialRender, is2ndPass){
34735         var g = this.grid;
34736         var auto = g.autoHeight;
34737         var scrollOffset = 16;
34738         var c = g.getGridEl(), cm = this.cm,
34739                 expandCol = g.autoExpandColumn,
34740                 gv = this;
34741         //c.beginMeasure();
34742
34743         if(!c.dom.offsetWidth){ // display:none?
34744             if(initialRender){
34745                 this.lockedWrap.show();
34746                 this.mainWrap.show();
34747             }
34748             return;
34749         }
34750
34751         var hasLock = this.cm.isLocked(0);
34752
34753         var tbh = this.headerPanel.getHeight();
34754         var bbh = this.footerPanel.getHeight();
34755
34756         if(auto){
34757             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34758             var newHeight = ch + c.getBorderWidth("tb");
34759             if(g.maxHeight){
34760                 newHeight = Math.min(g.maxHeight, newHeight);
34761             }
34762             c.setHeight(newHeight);
34763         }
34764
34765         if(g.autoWidth){
34766             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34767         }
34768
34769         var s = this.scroller;
34770
34771         var csize = c.getSize(true);
34772
34773         this.el.setSize(csize.width, csize.height);
34774
34775         this.headerPanel.setWidth(csize.width);
34776         this.footerPanel.setWidth(csize.width);
34777
34778         var hdHeight = this.mainHd.getHeight();
34779         var vw = csize.width;
34780         var vh = csize.height - (tbh + bbh);
34781
34782         s.setSize(vw, vh);
34783
34784         var bt = this.getBodyTable();
34785         
34786         if(cm.getLockedCount() == cm.config.length){
34787             bt = this.getLockedTable();
34788         }
34789         
34790         var ltWidth = hasLock ?
34791                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34792
34793         var scrollHeight = bt.offsetHeight;
34794         var scrollWidth = ltWidth + bt.offsetWidth;
34795         var vscroll = false, hscroll = false;
34796
34797         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34798
34799         var lw = this.lockedWrap, mw = this.mainWrap;
34800         var lb = this.lockedBody, mb = this.mainBody;
34801
34802         setTimeout(function(){
34803             var t = s.dom.offsetTop;
34804             var w = s.dom.clientWidth,
34805                 h = s.dom.clientHeight;
34806
34807             lw.setTop(t);
34808             lw.setSize(ltWidth, h);
34809
34810             mw.setLeftTop(ltWidth, t);
34811             mw.setSize(w-ltWidth, h);
34812
34813             lb.setHeight(h-hdHeight);
34814             mb.setHeight(h-hdHeight);
34815
34816             if(is2ndPass !== true && !gv.userResized && expandCol){
34817                 // high speed resize without full column calculation
34818                 
34819                 var ci = cm.getIndexById(expandCol);
34820                 if (ci < 0) {
34821                     ci = cm.findColumnIndex(expandCol);
34822                 }
34823                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34824                 var expandId = cm.getColumnId(ci);
34825                 var  tw = cm.getTotalWidth(false);
34826                 var currentWidth = cm.getColumnWidth(ci);
34827                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34828                 if(currentWidth != cw){
34829                     cm.setColumnWidth(ci, cw, true);
34830                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34831                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34832                     gv.updateSplitters();
34833                     gv.layout(false, true);
34834                 }
34835             }
34836
34837             if(initialRender){
34838                 lw.show();
34839                 mw.show();
34840             }
34841             //c.endMeasure();
34842         }, 10);
34843     },
34844
34845     onWindowResize : function(){
34846         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34847             return;
34848         }
34849         this.layout();
34850     },
34851
34852     appendFooter : function(parentEl){
34853         return null;
34854     },
34855
34856     sortAscText : "Sort Ascending",
34857     sortDescText : "Sort Descending",
34858     lockText : "Lock Column",
34859     unlockText : "Unlock Column",
34860     columnsText : "Columns",
34861  
34862     columnsWiderText : "Wider",
34863     columnsNarrowText : "Thinner"
34864 });
34865
34866
34867 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34868     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34869     this.proxy.el.addClass('x-grid3-col-dd');
34870 };
34871
34872 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34873     handleMouseDown : function(e){
34874
34875     },
34876
34877     callHandleMouseDown : function(e){
34878         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34879     }
34880 });
34881 /*
34882  * Based on:
34883  * Ext JS Library 1.1.1
34884  * Copyright(c) 2006-2007, Ext JS, LLC.
34885  *
34886  * Originally Released Under LGPL - original licence link has changed is not relivant.
34887  *
34888  * Fork - LGPL
34889  * <script type="text/javascript">
34890  */
34891  
34892 // private
34893 // This is a support class used internally by the Grid components
34894 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34895     this.grid = grid;
34896     this.view = grid.getView();
34897     this.proxy = this.view.resizeProxy;
34898     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34899         "gridSplitters" + this.grid.getGridEl().id, {
34900         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34901     });
34902     this.setHandleElId(Roo.id(hd));
34903     this.setOuterHandleElId(Roo.id(hd2));
34904     this.scroll = false;
34905 };
34906 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34907     fly: Roo.Element.fly,
34908
34909     b4StartDrag : function(x, y){
34910         this.view.headersDisabled = true;
34911         this.proxy.setHeight(this.view.mainWrap.getHeight());
34912         var w = this.cm.getColumnWidth(this.cellIndex);
34913         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34914         this.resetConstraints();
34915         this.setXConstraint(minw, 1000);
34916         this.setYConstraint(0, 0);
34917         this.minX = x - minw;
34918         this.maxX = x + 1000;
34919         this.startPos = x;
34920         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34921     },
34922
34923
34924     handleMouseDown : function(e){
34925         ev = Roo.EventObject.setEvent(e);
34926         var t = this.fly(ev.getTarget());
34927         if(t.hasClass("x-grid-split")){
34928             this.cellIndex = this.view.getCellIndex(t.dom);
34929             this.split = t.dom;
34930             this.cm = this.grid.colModel;
34931             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34932                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34933             }
34934         }
34935     },
34936
34937     endDrag : function(e){
34938         this.view.headersDisabled = false;
34939         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34940         var diff = endX - this.startPos;
34941         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34942     },
34943
34944     autoOffset : function(){
34945         this.setDelta(0,0);
34946     }
34947 });/*
34948  * Based on:
34949  * Ext JS Library 1.1.1
34950  * Copyright(c) 2006-2007, Ext JS, LLC.
34951  *
34952  * Originally Released Under LGPL - original licence link has changed is not relivant.
34953  *
34954  * Fork - LGPL
34955  * <script type="text/javascript">
34956  */
34957  
34958 // private
34959 // This is a support class used internally by the Grid components
34960 Roo.grid.GridDragZone = function(grid, config){
34961     this.view = grid.getView();
34962     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34963     if(this.view.lockedBody){
34964         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34965         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34966     }
34967     this.scroll = false;
34968     this.grid = grid;
34969     this.ddel = document.createElement('div');
34970     this.ddel.className = 'x-grid-dd-wrap';
34971 };
34972
34973 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34974     ddGroup : "GridDD",
34975
34976     getDragData : function(e){
34977         var t = Roo.lib.Event.getTarget(e);
34978         var rowIndex = this.view.findRowIndex(t);
34979         var sm = this.grid.selModel;
34980             
34981         //Roo.log(rowIndex);
34982         
34983         if (sm.getSelectedCell) {
34984             // cell selection..
34985             if (!sm.getSelectedCell()) {
34986                 return false;
34987             }
34988             if (rowIndex != sm.getSelectedCell()[0]) {
34989                 return false;
34990             }
34991         
34992         }
34993         
34994         if(rowIndex !== false){
34995             
34996             // if editorgrid.. 
34997             
34998             
34999             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
35000                
35001             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
35002               //  
35003             //}
35004             if (e.hasModifier()){
35005                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35006             }
35007             
35008             Roo.log("getDragData");
35009             
35010             return {
35011                 grid: this.grid,
35012                 ddel: this.ddel,
35013                 rowIndex: rowIndex,
35014                 selections:sm.getSelections ? sm.getSelections() : (
35015                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35016                 )
35017             };
35018         }
35019         return false;
35020     },
35021
35022     onInitDrag : function(e){
35023         var data = this.dragData;
35024         this.ddel.innerHTML = this.grid.getDragDropText();
35025         this.proxy.update(this.ddel);
35026         // fire start drag?
35027     },
35028
35029     afterRepair : function(){
35030         this.dragging = false;
35031     },
35032
35033     getRepairXY : function(e, data){
35034         return false;
35035     },
35036
35037     onEndDrag : function(data, e){
35038         // fire end drag?
35039     },
35040
35041     onValidDrop : function(dd, e, id){
35042         // fire drag drop?
35043         this.hideProxy();
35044     },
35045
35046     beforeInvalidDrop : function(e, id){
35047
35048     }
35049 });/*
35050  * Based on:
35051  * Ext JS Library 1.1.1
35052  * Copyright(c) 2006-2007, Ext JS, LLC.
35053  *
35054  * Originally Released Under LGPL - original licence link has changed is not relivant.
35055  *
35056  * Fork - LGPL
35057  * <script type="text/javascript">
35058  */
35059  
35060
35061 /**
35062  * @class Roo.grid.ColumnModel
35063  * @extends Roo.util.Observable
35064  * This is the default implementation of a ColumnModel used by the Grid. It defines
35065  * the columns in the grid.
35066  * <br>Usage:<br>
35067  <pre><code>
35068  var colModel = new Roo.grid.ColumnModel([
35069         {header: "Ticker", width: 60, sortable: true, locked: true},
35070         {header: "Company Name", width: 150, sortable: true},
35071         {header: "Market Cap.", width: 100, sortable: true},
35072         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35073         {header: "Employees", width: 100, sortable: true, resizable: false}
35074  ]);
35075  </code></pre>
35076  * <p>
35077  
35078  * The config options listed for this class are options which may appear in each
35079  * individual column definition.
35080  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35081  * @constructor
35082  * @param {Object} config An Array of column config objects. See this class's
35083  * config objects for details.
35084 */
35085 Roo.grid.ColumnModel = function(config){
35086         /**
35087      * The config passed into the constructor
35088      */
35089     this.config = config;
35090     this.lookup = {};
35091
35092     // if no id, create one
35093     // if the column does not have a dataIndex mapping,
35094     // map it to the order it is in the config
35095     for(var i = 0, len = config.length; i < len; i++){
35096         var c = config[i];
35097         if(typeof c.dataIndex == "undefined"){
35098             c.dataIndex = i;
35099         }
35100         if(typeof c.renderer == "string"){
35101             c.renderer = Roo.util.Format[c.renderer];
35102         }
35103         if(typeof c.id == "undefined"){
35104             c.id = Roo.id();
35105         }
35106         if(c.editor && c.editor.xtype){
35107             c.editor  = Roo.factory(c.editor, Roo.grid);
35108         }
35109         if(c.editor && c.editor.isFormField){
35110             c.editor = new Roo.grid.GridEditor(c.editor);
35111         }
35112         this.lookup[c.id] = c;
35113     }
35114
35115     /**
35116      * The width of columns which have no width specified (defaults to 100)
35117      * @type Number
35118      */
35119     this.defaultWidth = 100;
35120
35121     /**
35122      * Default sortable of columns which have no sortable specified (defaults to false)
35123      * @type Boolean
35124      */
35125     this.defaultSortable = false;
35126
35127     this.addEvents({
35128         /**
35129              * @event widthchange
35130              * Fires when the width of a column changes.
35131              * @param {ColumnModel} this
35132              * @param {Number} columnIndex The column index
35133              * @param {Number} newWidth The new width
35134              */
35135             "widthchange": true,
35136         /**
35137              * @event headerchange
35138              * Fires when the text of a header changes.
35139              * @param {ColumnModel} this
35140              * @param {Number} columnIndex The column index
35141              * @param {Number} newText The new header text
35142              */
35143             "headerchange": true,
35144         /**
35145              * @event hiddenchange
35146              * Fires when a column is hidden or "unhidden".
35147              * @param {ColumnModel} this
35148              * @param {Number} columnIndex The column index
35149              * @param {Boolean} hidden true if hidden, false otherwise
35150              */
35151             "hiddenchange": true,
35152             /**
35153          * @event columnmoved
35154          * Fires when a column is moved.
35155          * @param {ColumnModel} this
35156          * @param {Number} oldIndex
35157          * @param {Number} newIndex
35158          */
35159         "columnmoved" : true,
35160         /**
35161          * @event columlockchange
35162          * Fires when a column's locked state is changed
35163          * @param {ColumnModel} this
35164          * @param {Number} colIndex
35165          * @param {Boolean} locked true if locked
35166          */
35167         "columnlockchange" : true
35168     });
35169     Roo.grid.ColumnModel.superclass.constructor.call(this);
35170 };
35171 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35172     /**
35173      * @cfg {String} header The header text to display in the Grid view.
35174      */
35175     /**
35176      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35177      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35178      * specified, the column's index is used as an index into the Record's data Array.
35179      */
35180     /**
35181      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35182      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35183      */
35184     /**
35185      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35186      * Defaults to the value of the {@link #defaultSortable} property.
35187      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35188      */
35189     /**
35190      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35191      */
35192     /**
35193      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35194      */
35195     /**
35196      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35197      */
35198     /**
35199      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35200      */
35201     /**
35202      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35203      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35204      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35205      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35206      */
35207        /**
35208      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35209      */
35210     /**
35211      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35212      */
35213     /**
35214      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35215      */
35216     /**
35217      * @cfg {String} cursor (Optional)
35218      */
35219     /**
35220      * @cfg {String} tooltip (Optional)
35221      */
35222     /**
35223      * @cfg {Number} xs (Optional)
35224      */
35225     /**
35226      * @cfg {Number} sm (Optional)
35227      */
35228     /**
35229      * @cfg {Number} md (Optional)
35230      */
35231     /**
35232      * @cfg {Number} lg (Optional)
35233      */
35234     /**
35235      * Returns the id of the column at the specified index.
35236      * @param {Number} index The column index
35237      * @return {String} the id
35238      */
35239     getColumnId : function(index){
35240         return this.config[index].id;
35241     },
35242
35243     /**
35244      * Returns the column for a specified id.
35245      * @param {String} id The column id
35246      * @return {Object} the column
35247      */
35248     getColumnById : function(id){
35249         return this.lookup[id];
35250     },
35251
35252     
35253     /**
35254      * Returns the column for a specified dataIndex.
35255      * @param {String} dataIndex The column dataIndex
35256      * @return {Object|Boolean} the column or false if not found
35257      */
35258     getColumnByDataIndex: function(dataIndex){
35259         var index = this.findColumnIndex(dataIndex);
35260         return index > -1 ? this.config[index] : false;
35261     },
35262     
35263     /**
35264      * Returns the index for a specified column id.
35265      * @param {String} id The column id
35266      * @return {Number} the index, or -1 if not found
35267      */
35268     getIndexById : function(id){
35269         for(var i = 0, len = this.config.length; i < len; i++){
35270             if(this.config[i].id == id){
35271                 return i;
35272             }
35273         }
35274         return -1;
35275     },
35276     
35277     /**
35278      * Returns the index for a specified column dataIndex.
35279      * @param {String} dataIndex The column dataIndex
35280      * @return {Number} the index, or -1 if not found
35281      */
35282     
35283     findColumnIndex : function(dataIndex){
35284         for(var i = 0, len = this.config.length; i < len; i++){
35285             if(this.config[i].dataIndex == dataIndex){
35286                 return i;
35287             }
35288         }
35289         return -1;
35290     },
35291     
35292     
35293     moveColumn : function(oldIndex, newIndex){
35294         var c = this.config[oldIndex];
35295         this.config.splice(oldIndex, 1);
35296         this.config.splice(newIndex, 0, c);
35297         this.dataMap = null;
35298         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35299     },
35300
35301     isLocked : function(colIndex){
35302         return this.config[colIndex].locked === true;
35303     },
35304
35305     setLocked : function(colIndex, value, suppressEvent){
35306         if(this.isLocked(colIndex) == value){
35307             return;
35308         }
35309         this.config[colIndex].locked = value;
35310         if(!suppressEvent){
35311             this.fireEvent("columnlockchange", this, colIndex, value);
35312         }
35313     },
35314
35315     getTotalLockedWidth : function(){
35316         var totalWidth = 0;
35317         for(var i = 0; i < this.config.length; i++){
35318             if(this.isLocked(i) && !this.isHidden(i)){
35319                 this.totalWidth += this.getColumnWidth(i);
35320             }
35321         }
35322         return totalWidth;
35323     },
35324
35325     getLockedCount : function(){
35326         for(var i = 0, len = this.config.length; i < len; i++){
35327             if(!this.isLocked(i)){
35328                 return i;
35329             }
35330         }
35331         
35332         return this.config.length;
35333     },
35334
35335     /**
35336      * Returns the number of columns.
35337      * @return {Number}
35338      */
35339     getColumnCount : function(visibleOnly){
35340         if(visibleOnly === true){
35341             var c = 0;
35342             for(var i = 0, len = this.config.length; i < len; i++){
35343                 if(!this.isHidden(i)){
35344                     c++;
35345                 }
35346             }
35347             return c;
35348         }
35349         return this.config.length;
35350     },
35351
35352     /**
35353      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35354      * @param {Function} fn
35355      * @param {Object} scope (optional)
35356      * @return {Array} result
35357      */
35358     getColumnsBy : function(fn, scope){
35359         var r = [];
35360         for(var i = 0, len = this.config.length; i < len; i++){
35361             var c = this.config[i];
35362             if(fn.call(scope||this, c, i) === true){
35363                 r[r.length] = c;
35364             }
35365         }
35366         return r;
35367     },
35368
35369     /**
35370      * Returns true if the specified column is sortable.
35371      * @param {Number} col The column index
35372      * @return {Boolean}
35373      */
35374     isSortable : function(col){
35375         if(typeof this.config[col].sortable == "undefined"){
35376             return this.defaultSortable;
35377         }
35378         return this.config[col].sortable;
35379     },
35380
35381     /**
35382      * Returns the rendering (formatting) function defined for the column.
35383      * @param {Number} col The column index.
35384      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35385      */
35386     getRenderer : function(col){
35387         if(!this.config[col].renderer){
35388             return Roo.grid.ColumnModel.defaultRenderer;
35389         }
35390         return this.config[col].renderer;
35391     },
35392
35393     /**
35394      * Sets the rendering (formatting) function for a column.
35395      * @param {Number} col The column index
35396      * @param {Function} fn The function to use to process the cell's raw data
35397      * to return HTML markup for the grid view. The render function is called with
35398      * the following parameters:<ul>
35399      * <li>Data value.</li>
35400      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35401      * <li>css A CSS style string to apply to the table cell.</li>
35402      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35403      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35404      * <li>Row index</li>
35405      * <li>Column index</li>
35406      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35407      */
35408     setRenderer : function(col, fn){
35409         this.config[col].renderer = fn;
35410     },
35411
35412     /**
35413      * Returns the width for the specified column.
35414      * @param {Number} col The column index
35415      * @return {Number}
35416      */
35417     getColumnWidth : function(col){
35418         return this.config[col].width * 1 || this.defaultWidth;
35419     },
35420
35421     /**
35422      * Sets the width for a column.
35423      * @param {Number} col The column index
35424      * @param {Number} width The new width
35425      */
35426     setColumnWidth : function(col, width, suppressEvent){
35427         this.config[col].width = width;
35428         this.totalWidth = null;
35429         if(!suppressEvent){
35430              this.fireEvent("widthchange", this, col, width);
35431         }
35432     },
35433
35434     /**
35435      * Returns the total width of all columns.
35436      * @param {Boolean} includeHidden True to include hidden column widths
35437      * @return {Number}
35438      */
35439     getTotalWidth : function(includeHidden){
35440         if(!this.totalWidth){
35441             this.totalWidth = 0;
35442             for(var i = 0, len = this.config.length; i < len; i++){
35443                 if(includeHidden || !this.isHidden(i)){
35444                     this.totalWidth += this.getColumnWidth(i);
35445                 }
35446             }
35447         }
35448         return this.totalWidth;
35449     },
35450
35451     /**
35452      * Returns the header for the specified column.
35453      * @param {Number} col The column index
35454      * @return {String}
35455      */
35456     getColumnHeader : function(col){
35457         return this.config[col].header;
35458     },
35459
35460     /**
35461      * Sets the header for a column.
35462      * @param {Number} col The column index
35463      * @param {String} header The new header
35464      */
35465     setColumnHeader : function(col, header){
35466         this.config[col].header = header;
35467         this.fireEvent("headerchange", this, col, header);
35468     },
35469
35470     /**
35471      * Returns the tooltip for the specified column.
35472      * @param {Number} col The column index
35473      * @return {String}
35474      */
35475     getColumnTooltip : function(col){
35476             return this.config[col].tooltip;
35477     },
35478     /**
35479      * Sets the tooltip for a column.
35480      * @param {Number} col The column index
35481      * @param {String} tooltip The new tooltip
35482      */
35483     setColumnTooltip : function(col, tooltip){
35484             this.config[col].tooltip = tooltip;
35485     },
35486
35487     /**
35488      * Returns the dataIndex for the specified column.
35489      * @param {Number} col The column index
35490      * @return {Number}
35491      */
35492     getDataIndex : function(col){
35493         return this.config[col].dataIndex;
35494     },
35495
35496     /**
35497      * Sets the dataIndex for a column.
35498      * @param {Number} col The column index
35499      * @param {Number} dataIndex The new dataIndex
35500      */
35501     setDataIndex : function(col, dataIndex){
35502         this.config[col].dataIndex = dataIndex;
35503     },
35504
35505     
35506     
35507     /**
35508      * Returns true if the cell is editable.
35509      * @param {Number} colIndex The column index
35510      * @param {Number} rowIndex The row index - this is nto actually used..?
35511      * @return {Boolean}
35512      */
35513     isCellEditable : function(colIndex, rowIndex){
35514         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35515     },
35516
35517     /**
35518      * Returns the editor defined for the cell/column.
35519      * return false or null to disable editing.
35520      * @param {Number} colIndex The column index
35521      * @param {Number} rowIndex The row index
35522      * @return {Object}
35523      */
35524     getCellEditor : function(colIndex, rowIndex){
35525         return this.config[colIndex].editor;
35526     },
35527
35528     /**
35529      * Sets if a column is editable.
35530      * @param {Number} col The column index
35531      * @param {Boolean} editable True if the column is editable
35532      */
35533     setEditable : function(col, editable){
35534         this.config[col].editable = editable;
35535     },
35536
35537
35538     /**
35539      * Returns true if the column is hidden.
35540      * @param {Number} colIndex The column index
35541      * @return {Boolean}
35542      */
35543     isHidden : function(colIndex){
35544         return this.config[colIndex].hidden;
35545     },
35546
35547
35548     /**
35549      * Returns true if the column width cannot be changed
35550      */
35551     isFixed : function(colIndex){
35552         return this.config[colIndex].fixed;
35553     },
35554
35555     /**
35556      * Returns true if the column can be resized
35557      * @return {Boolean}
35558      */
35559     isResizable : function(colIndex){
35560         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35561     },
35562     /**
35563      * Sets if a column is hidden.
35564      * @param {Number} colIndex The column index
35565      * @param {Boolean} hidden True if the column is hidden
35566      */
35567     setHidden : function(colIndex, hidden){
35568         this.config[colIndex].hidden = hidden;
35569         this.totalWidth = null;
35570         this.fireEvent("hiddenchange", this, colIndex, hidden);
35571     },
35572
35573     /**
35574      * Sets the editor for a column.
35575      * @param {Number} col The column index
35576      * @param {Object} editor The editor object
35577      */
35578     setEditor : function(col, editor){
35579         this.config[col].editor = editor;
35580     }
35581 });
35582
35583 Roo.grid.ColumnModel.defaultRenderer = function(value)
35584 {
35585     if(typeof value == "object") {
35586         return value;
35587     }
35588         if(typeof value == "string" && value.length < 1){
35589             return "&#160;";
35590         }
35591     
35592         return String.format("{0}", value);
35593 };
35594
35595 // Alias for backwards compatibility
35596 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35597 /*
35598  * Based on:
35599  * Ext JS Library 1.1.1
35600  * Copyright(c) 2006-2007, Ext JS, LLC.
35601  *
35602  * Originally Released Under LGPL - original licence link has changed is not relivant.
35603  *
35604  * Fork - LGPL
35605  * <script type="text/javascript">
35606  */
35607
35608 /**
35609  * @class Roo.grid.AbstractSelectionModel
35610  * @extends Roo.util.Observable
35611  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35612  * implemented by descendant classes.  This class should not be directly instantiated.
35613  * @constructor
35614  */
35615 Roo.grid.AbstractSelectionModel = function(){
35616     this.locked = false;
35617     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35618 };
35619
35620 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35621     /** @ignore Called by the grid automatically. Do not call directly. */
35622     init : function(grid){
35623         this.grid = grid;
35624         this.initEvents();
35625     },
35626
35627     /**
35628      * Locks the selections.
35629      */
35630     lock : function(){
35631         this.locked = true;
35632     },
35633
35634     /**
35635      * Unlocks the selections.
35636      */
35637     unlock : function(){
35638         this.locked = false;
35639     },
35640
35641     /**
35642      * Returns true if the selections are locked.
35643      * @return {Boolean}
35644      */
35645     isLocked : function(){
35646         return this.locked;
35647     }
35648 });/*
35649  * Based on:
35650  * Ext JS Library 1.1.1
35651  * Copyright(c) 2006-2007, Ext JS, LLC.
35652  *
35653  * Originally Released Under LGPL - original licence link has changed is not relivant.
35654  *
35655  * Fork - LGPL
35656  * <script type="text/javascript">
35657  */
35658 /**
35659  * @extends Roo.grid.AbstractSelectionModel
35660  * @class Roo.grid.RowSelectionModel
35661  * The default SelectionModel used by {@link Roo.grid.Grid}.
35662  * It supports multiple selections and keyboard selection/navigation. 
35663  * @constructor
35664  * @param {Object} config
35665  */
35666 Roo.grid.RowSelectionModel = function(config){
35667     Roo.apply(this, config);
35668     this.selections = new Roo.util.MixedCollection(false, function(o){
35669         return o.id;
35670     });
35671
35672     this.last = false;
35673     this.lastActive = false;
35674
35675     this.addEvents({
35676         /**
35677              * @event selectionchange
35678              * Fires when the selection changes
35679              * @param {SelectionModel} this
35680              */
35681             "selectionchange" : true,
35682         /**
35683              * @event afterselectionchange
35684              * Fires after the selection changes (eg. by key press or clicking)
35685              * @param {SelectionModel} this
35686              */
35687             "afterselectionchange" : true,
35688         /**
35689              * @event beforerowselect
35690              * Fires when a row is selected being selected, return false to cancel.
35691              * @param {SelectionModel} this
35692              * @param {Number} rowIndex The selected index
35693              * @param {Boolean} keepExisting False if other selections will be cleared
35694              */
35695             "beforerowselect" : true,
35696         /**
35697              * @event rowselect
35698              * Fires when a row is selected.
35699              * @param {SelectionModel} this
35700              * @param {Number} rowIndex The selected index
35701              * @param {Roo.data.Record} r The record
35702              */
35703             "rowselect" : true,
35704         /**
35705              * @event rowdeselect
35706              * Fires when a row is deselected.
35707              * @param {SelectionModel} this
35708              * @param {Number} rowIndex The selected index
35709              */
35710         "rowdeselect" : true
35711     });
35712     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35713     this.locked = false;
35714 };
35715
35716 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35717     /**
35718      * @cfg {Boolean} singleSelect
35719      * True to allow selection of only one row at a time (defaults to false)
35720      */
35721     singleSelect : false,
35722
35723     // private
35724     initEvents : function(){
35725
35726         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35727             this.grid.on("mousedown", this.handleMouseDown, this);
35728         }else{ // allow click to work like normal
35729             this.grid.on("rowclick", this.handleDragableRowClick, this);
35730         }
35731
35732         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35733             "up" : function(e){
35734                 if(!e.shiftKey){
35735                     this.selectPrevious(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             "down" : function(e){
35749                 if(!e.shiftKey){
35750                     this.selectNext(e.shiftKey);
35751                 }else if(this.last !== false && this.lastActive !== false){
35752                     var last = this.last;
35753                     this.selectRange(this.last,  this.lastActive+1);
35754                     this.grid.getView().focusRow(this.lastActive);
35755                     if(last !== false){
35756                         this.last = last;
35757                     }
35758                 }else{
35759                     this.selectFirstRow();
35760                 }
35761                 this.fireEvent("afterselectionchange", this);
35762             },
35763             scope: this
35764         });
35765
35766         var view = this.grid.view;
35767         view.on("refresh", this.onRefresh, this);
35768         view.on("rowupdated", this.onRowUpdated, this);
35769         view.on("rowremoved", this.onRemove, this);
35770     },
35771
35772     // private
35773     onRefresh : function(){
35774         var ds = this.grid.dataSource, i, v = this.grid.view;
35775         var s = this.selections;
35776         s.each(function(r){
35777             if((i = ds.indexOfId(r.id)) != -1){
35778                 v.onRowSelect(i);
35779                 s.add(ds.getAt(i)); // updating the selection relate data
35780             }else{
35781                 s.remove(r);
35782             }
35783         });
35784     },
35785
35786     // private
35787     onRemove : function(v, index, r){
35788         this.selections.remove(r);
35789     },
35790
35791     // private
35792     onRowUpdated : function(v, index, r){
35793         if(this.isSelected(r)){
35794             v.onRowSelect(index);
35795         }
35796     },
35797
35798     /**
35799      * Select records.
35800      * @param {Array} records The records to select
35801      * @param {Boolean} keepExisting (optional) True to keep existing selections
35802      */
35803     selectRecords : function(records, keepExisting){
35804         if(!keepExisting){
35805             this.clearSelections();
35806         }
35807         var ds = this.grid.dataSource;
35808         for(var i = 0, len = records.length; i < len; i++){
35809             this.selectRow(ds.indexOf(records[i]), true);
35810         }
35811     },
35812
35813     /**
35814      * Gets the number of selected rows.
35815      * @return {Number}
35816      */
35817     getCount : function(){
35818         return this.selections.length;
35819     },
35820
35821     /**
35822      * Selects the first row in the grid.
35823      */
35824     selectFirstRow : function(){
35825         this.selectRow(0);
35826     },
35827
35828     /**
35829      * Select the last row.
35830      * @param {Boolean} keepExisting (optional) True to keep existing selections
35831      */
35832     selectLastRow : function(keepExisting){
35833         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35834     },
35835
35836     /**
35837      * Selects the row immediately following the last selected row.
35838      * @param {Boolean} keepExisting (optional) True to keep existing selections
35839      */
35840     selectNext : function(keepExisting){
35841         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35842             this.selectRow(this.last+1, keepExisting);
35843             this.grid.getView().focusRow(this.last);
35844         }
35845     },
35846
35847     /**
35848      * Selects the row that precedes the last selected row.
35849      * @param {Boolean} keepExisting (optional) True to keep existing selections
35850      */
35851     selectPrevious : function(keepExisting){
35852         if(this.last){
35853             this.selectRow(this.last-1, keepExisting);
35854             this.grid.getView().focusRow(this.last);
35855         }
35856     },
35857
35858     /**
35859      * Returns the selected records
35860      * @return {Array} Array of selected records
35861      */
35862     getSelections : function(){
35863         return [].concat(this.selections.items);
35864     },
35865
35866     /**
35867      * Returns the first selected record.
35868      * @return {Record}
35869      */
35870     getSelected : function(){
35871         return this.selections.itemAt(0);
35872     },
35873
35874
35875     /**
35876      * Clears all selections.
35877      */
35878     clearSelections : function(fast){
35879         if(this.locked) {
35880             return;
35881         }
35882         if(fast !== true){
35883             var ds = this.grid.dataSource;
35884             var s = this.selections;
35885             s.each(function(r){
35886                 this.deselectRow(ds.indexOfId(r.id));
35887             }, this);
35888             s.clear();
35889         }else{
35890             this.selections.clear();
35891         }
35892         this.last = false;
35893     },
35894
35895
35896     /**
35897      * Selects all rows.
35898      */
35899     selectAll : function(){
35900         if(this.locked) {
35901             return;
35902         }
35903         this.selections.clear();
35904         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35905             this.selectRow(i, true);
35906         }
35907     },
35908
35909     /**
35910      * Returns True if there is a selection.
35911      * @return {Boolean}
35912      */
35913     hasSelection : function(){
35914         return this.selections.length > 0;
35915     },
35916
35917     /**
35918      * Returns True if the specified row is selected.
35919      * @param {Number/Record} record The record or index of the record to check
35920      * @return {Boolean}
35921      */
35922     isSelected : function(index){
35923         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35924         return (r && this.selections.key(r.id) ? true : false);
35925     },
35926
35927     /**
35928      * Returns True if the specified record id is selected.
35929      * @param {String} id The id of record to check
35930      * @return {Boolean}
35931      */
35932     isIdSelected : function(id){
35933         return (this.selections.key(id) ? true : false);
35934     },
35935
35936     // private
35937     handleMouseDown : function(e, t){
35938         var view = this.grid.getView(), rowIndex;
35939         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35940             return;
35941         };
35942         if(e.shiftKey && this.last !== false){
35943             var last = this.last;
35944             this.selectRange(last, rowIndex, e.ctrlKey);
35945             this.last = last; // reset the last
35946             view.focusRow(rowIndex);
35947         }else{
35948             var isSelected = this.isSelected(rowIndex);
35949             if(e.button !== 0 && isSelected){
35950                 view.focusRow(rowIndex);
35951             }else if(e.ctrlKey && isSelected){
35952                 this.deselectRow(rowIndex);
35953             }else if(!isSelected){
35954                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35955                 view.focusRow(rowIndex);
35956             }
35957         }
35958         this.fireEvent("afterselectionchange", this);
35959     },
35960     // private
35961     handleDragableRowClick :  function(grid, rowIndex, e) 
35962     {
35963         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35964             this.selectRow(rowIndex, false);
35965             grid.view.focusRow(rowIndex);
35966              this.fireEvent("afterselectionchange", this);
35967         }
35968     },
35969     
35970     /**
35971      * Selects multiple rows.
35972      * @param {Array} rows Array of the indexes of the row to select
35973      * @param {Boolean} keepExisting (optional) True to keep existing selections
35974      */
35975     selectRows : function(rows, keepExisting){
35976         if(!keepExisting){
35977             this.clearSelections();
35978         }
35979         for(var i = 0, len = rows.length; i < len; i++){
35980             this.selectRow(rows[i], true);
35981         }
35982     },
35983
35984     /**
35985      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35986      * @param {Number} startRow The index of the first row in the range
35987      * @param {Number} endRow The index of the last row in the range
35988      * @param {Boolean} keepExisting (optional) True to retain existing selections
35989      */
35990     selectRange : function(startRow, endRow, keepExisting){
35991         if(this.locked) {
35992             return;
35993         }
35994         if(!keepExisting){
35995             this.clearSelections();
35996         }
35997         if(startRow <= endRow){
35998             for(var i = startRow; i <= endRow; i++){
35999                 this.selectRow(i, true);
36000             }
36001         }else{
36002             for(var i = startRow; i >= endRow; i--){
36003                 this.selectRow(i, true);
36004             }
36005         }
36006     },
36007
36008     /**
36009      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36010      * @param {Number} startRow The index of the first row in the range
36011      * @param {Number} endRow The index of the last row in the range
36012      */
36013     deselectRange : function(startRow, endRow, preventViewNotify){
36014         if(this.locked) {
36015             return;
36016         }
36017         for(var i = startRow; i <= endRow; i++){
36018             this.deselectRow(i, preventViewNotify);
36019         }
36020     },
36021
36022     /**
36023      * Selects a row.
36024      * @param {Number} row The index of the row to select
36025      * @param {Boolean} keepExisting (optional) True to keep existing selections
36026      */
36027     selectRow : function(index, keepExisting, preventViewNotify){
36028         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36029             return;
36030         }
36031         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36032             if(!keepExisting || this.singleSelect){
36033                 this.clearSelections();
36034             }
36035             var r = this.grid.dataSource.getAt(index);
36036             this.selections.add(r);
36037             this.last = this.lastActive = index;
36038             if(!preventViewNotify){
36039                 this.grid.getView().onRowSelect(index);
36040             }
36041             this.fireEvent("rowselect", this, index, r);
36042             this.fireEvent("selectionchange", this);
36043         }
36044     },
36045
36046     /**
36047      * Deselects a row.
36048      * @param {Number} row The index of the row to deselect
36049      */
36050     deselectRow : function(index, preventViewNotify){
36051         if(this.locked) {
36052             return;
36053         }
36054         if(this.last == index){
36055             this.last = false;
36056         }
36057         if(this.lastActive == index){
36058             this.lastActive = false;
36059         }
36060         var r = this.grid.dataSource.getAt(index);
36061         this.selections.remove(r);
36062         if(!preventViewNotify){
36063             this.grid.getView().onRowDeselect(index);
36064         }
36065         this.fireEvent("rowdeselect", this, index);
36066         this.fireEvent("selectionchange", this);
36067     },
36068
36069     // private
36070     restoreLast : function(){
36071         if(this._last){
36072             this.last = this._last;
36073         }
36074     },
36075
36076     // private
36077     acceptsNav : function(row, col, cm){
36078         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36079     },
36080
36081     // private
36082     onEditorKey : function(field, e){
36083         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36084         if(k == e.TAB){
36085             e.stopEvent();
36086             ed.completeEdit();
36087             if(e.shiftKey){
36088                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36089             }else{
36090                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36091             }
36092         }else if(k == e.ENTER && !e.ctrlKey){
36093             e.stopEvent();
36094             ed.completeEdit();
36095             if(e.shiftKey){
36096                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36097             }else{
36098                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36099             }
36100         }else if(k == e.ESC){
36101             ed.cancelEdit();
36102         }
36103         if(newCell){
36104             g.startEditing(newCell[0], newCell[1]);
36105         }
36106     }
36107 });/*
36108  * Based on:
36109  * Ext JS Library 1.1.1
36110  * Copyright(c) 2006-2007, Ext JS, LLC.
36111  *
36112  * Originally Released Under LGPL - original licence link has changed is not relivant.
36113  *
36114  * Fork - LGPL
36115  * <script type="text/javascript">
36116  */
36117 /**
36118  * @class Roo.grid.CellSelectionModel
36119  * @extends Roo.grid.AbstractSelectionModel
36120  * This class provides the basic implementation for cell selection in a grid.
36121  * @constructor
36122  * @param {Object} config The object containing the configuration of this model.
36123  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36124  */
36125 Roo.grid.CellSelectionModel = function(config){
36126     Roo.apply(this, config);
36127
36128     this.selection = null;
36129
36130     this.addEvents({
36131         /**
36132              * @event beforerowselect
36133              * Fires before a cell is selected.
36134              * @param {SelectionModel} this
36135              * @param {Number} rowIndex The selected row index
36136              * @param {Number} colIndex The selected cell index
36137              */
36138             "beforecellselect" : true,
36139         /**
36140              * @event cellselect
36141              * Fires when a cell is selected.
36142              * @param {SelectionModel} this
36143              * @param {Number} rowIndex The selected row index
36144              * @param {Number} colIndex The selected cell index
36145              */
36146             "cellselect" : true,
36147         /**
36148              * @event selectionchange
36149              * Fires when the active selection changes.
36150              * @param {SelectionModel} this
36151              * @param {Object} selection null for no selection or an object (o) with two properties
36152                 <ul>
36153                 <li>o.record: the record object for the row the selection is in</li>
36154                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36155                 </ul>
36156              */
36157             "selectionchange" : true,
36158         /**
36159              * @event tabend
36160              * Fires when the tab (or enter) was pressed on the last editable cell
36161              * You can use this to trigger add new row.
36162              * @param {SelectionModel} this
36163              */
36164             "tabend" : true,
36165          /**
36166              * @event beforeeditnext
36167              * Fires before the next editable sell is made active
36168              * You can use this to skip to another cell or fire the tabend
36169              *    if you set cell to false
36170              * @param {Object} eventdata object : { cell : [ row, col ] } 
36171              */
36172             "beforeeditnext" : true
36173     });
36174     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36175 };
36176
36177 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36178     
36179     enter_is_tab: false,
36180
36181     /** @ignore */
36182     initEvents : function(){
36183         this.grid.on("mousedown", this.handleMouseDown, this);
36184         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36185         var view = this.grid.view;
36186         view.on("refresh", this.onViewChange, this);
36187         view.on("rowupdated", this.onRowUpdated, this);
36188         view.on("beforerowremoved", this.clearSelections, this);
36189         view.on("beforerowsinserted", this.clearSelections, this);
36190         if(this.grid.isEditor){
36191             this.grid.on("beforeedit", this.beforeEdit,  this);
36192         }
36193     },
36194
36195         //private
36196     beforeEdit : function(e){
36197         this.select(e.row, e.column, false, true, e.record);
36198     },
36199
36200         //private
36201     onRowUpdated : function(v, index, r){
36202         if(this.selection && this.selection.record == r){
36203             v.onCellSelect(index, this.selection.cell[1]);
36204         }
36205     },
36206
36207         //private
36208     onViewChange : function(){
36209         this.clearSelections(true);
36210     },
36211
36212         /**
36213          * Returns the currently selected cell,.
36214          * @return {Array} The selected cell (row, column) or null if none selected.
36215          */
36216     getSelectedCell : function(){
36217         return this.selection ? this.selection.cell : null;
36218     },
36219
36220     /**
36221      * Clears all selections.
36222      * @param {Boolean} true to prevent the gridview from being notified about the change.
36223      */
36224     clearSelections : function(preventNotify){
36225         var s = this.selection;
36226         if(s){
36227             if(preventNotify !== true){
36228                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36229             }
36230             this.selection = null;
36231             this.fireEvent("selectionchange", this, null);
36232         }
36233     },
36234
36235     /**
36236      * Returns true if there is a selection.
36237      * @return {Boolean}
36238      */
36239     hasSelection : function(){
36240         return this.selection ? true : false;
36241     },
36242
36243     /** @ignore */
36244     handleMouseDown : function(e, t){
36245         var v = this.grid.getView();
36246         if(this.isLocked()){
36247             return;
36248         };
36249         var row = v.findRowIndex(t);
36250         var cell = v.findCellIndex(t);
36251         if(row !== false && cell !== false){
36252             this.select(row, cell);
36253         }
36254     },
36255
36256     /**
36257      * Selects a cell.
36258      * @param {Number} rowIndex
36259      * @param {Number} collIndex
36260      */
36261     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36262         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36263             this.clearSelections();
36264             r = r || this.grid.dataSource.getAt(rowIndex);
36265             this.selection = {
36266                 record : r,
36267                 cell : [rowIndex, colIndex]
36268             };
36269             if(!preventViewNotify){
36270                 var v = this.grid.getView();
36271                 v.onCellSelect(rowIndex, colIndex);
36272                 if(preventFocus !== true){
36273                     v.focusCell(rowIndex, colIndex);
36274                 }
36275             }
36276             this.fireEvent("cellselect", this, rowIndex, colIndex);
36277             this.fireEvent("selectionchange", this, this.selection);
36278         }
36279     },
36280
36281         //private
36282     isSelectable : function(rowIndex, colIndex, cm){
36283         return !cm.isHidden(colIndex);
36284     },
36285
36286     /** @ignore */
36287     handleKeyDown : function(e){
36288         //Roo.log('Cell Sel Model handleKeyDown');
36289         if(!e.isNavKeyPress()){
36290             return;
36291         }
36292         var g = this.grid, s = this.selection;
36293         if(!s){
36294             e.stopEvent();
36295             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36296             if(cell){
36297                 this.select(cell[0], cell[1]);
36298             }
36299             return;
36300         }
36301         var sm = this;
36302         var walk = function(row, col, step){
36303             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36304         };
36305         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36306         var newCell;
36307
36308       
36309
36310         switch(k){
36311             case e.TAB:
36312                 // handled by onEditorKey
36313                 if (g.isEditor && g.editing) {
36314                     return;
36315                 }
36316                 if(e.shiftKey) {
36317                     newCell = walk(r, c-1, -1);
36318                 } else {
36319                     newCell = walk(r, c+1, 1);
36320                 }
36321                 break;
36322             
36323             case e.DOWN:
36324                newCell = walk(r+1, c, 1);
36325                 break;
36326             
36327             case e.UP:
36328                 newCell = walk(r-1, c, -1);
36329                 break;
36330             
36331             case e.RIGHT:
36332                 newCell = walk(r, c+1, 1);
36333                 break;
36334             
36335             case e.LEFT:
36336                 newCell = walk(r, c-1, -1);
36337                 break;
36338             
36339             case e.ENTER:
36340                 
36341                 if(g.isEditor && !g.editing){
36342                    g.startEditing(r, c);
36343                    e.stopEvent();
36344                    return;
36345                 }
36346                 
36347                 
36348              break;
36349         };
36350         if(newCell){
36351             this.select(newCell[0], newCell[1]);
36352             e.stopEvent();
36353             
36354         }
36355     },
36356
36357     acceptsNav : function(row, col, cm){
36358         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36359     },
36360     /**
36361      * Selects a cell.
36362      * @param {Number} field (not used) - as it's normally used as a listener
36363      * @param {Number} e - event - fake it by using
36364      *
36365      * var e = Roo.EventObjectImpl.prototype;
36366      * e.keyCode = e.TAB
36367      *
36368      * 
36369      */
36370     onEditorKey : function(field, e){
36371         
36372         var k = e.getKey(),
36373             newCell,
36374             g = this.grid,
36375             ed = g.activeEditor,
36376             forward = false;
36377         ///Roo.log('onEditorKey' + k);
36378         
36379         
36380         if (this.enter_is_tab && k == e.ENTER) {
36381             k = e.TAB;
36382         }
36383         
36384         if(k == e.TAB){
36385             if(e.shiftKey){
36386                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36387             }else{
36388                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36389                 forward = true;
36390             }
36391             
36392             e.stopEvent();
36393             
36394         } else if(k == e.ENTER &&  !e.ctrlKey){
36395             ed.completeEdit();
36396             e.stopEvent();
36397             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36398         
36399                 } else if(k == e.ESC){
36400             ed.cancelEdit();
36401         }
36402                 
36403         if (newCell) {
36404             var ecall = { cell : newCell, forward : forward };
36405             this.fireEvent('beforeeditnext', ecall );
36406             newCell = ecall.cell;
36407                         forward = ecall.forward;
36408         }
36409                 
36410         if(newCell){
36411             //Roo.log('next cell after edit');
36412             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36413         } else if (forward) {
36414             // tabbed past last
36415             this.fireEvent.defer(100, this, ['tabend',this]);
36416         }
36417     }
36418 });/*
36419  * Based on:
36420  * Ext JS Library 1.1.1
36421  * Copyright(c) 2006-2007, Ext JS, LLC.
36422  *
36423  * Originally Released Under LGPL - original licence link has changed is not relivant.
36424  *
36425  * Fork - LGPL
36426  * <script type="text/javascript">
36427  */
36428  
36429 /**
36430  * @class Roo.grid.EditorGrid
36431  * @extends Roo.grid.Grid
36432  * Class for creating and editable grid.
36433  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36434  * The container MUST have some type of size defined for the grid to fill. The container will be 
36435  * automatically set to position relative if it isn't already.
36436  * @param {Object} dataSource The data model to bind to
36437  * @param {Object} colModel The column model with info about this grid's columns
36438  */
36439 Roo.grid.EditorGrid = function(container, config){
36440     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36441     this.getGridEl().addClass("xedit-grid");
36442
36443     if(!this.selModel){
36444         this.selModel = new Roo.grid.CellSelectionModel();
36445     }
36446
36447     this.activeEditor = null;
36448
36449         this.addEvents({
36450             /**
36451              * @event beforeedit
36452              * Fires before cell editing is triggered. The edit event object has the following properties <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 for the field being edited.</li>
36458              * <li>row - The grid row index</li>
36459              * <li>column - The grid column index</li>
36460              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36461              * </ul>
36462              * @param {Object} e An edit event (see above for description)
36463              */
36464             "beforeedit" : true,
36465             /**
36466              * @event afteredit
36467              * Fires after a cell is edited. <br />
36468              * <ul style="padding:5px;padding-left:16px;">
36469              * <li>grid - This grid</li>
36470              * <li>record - The record being edited</li>
36471              * <li>field - The field name being edited</li>
36472              * <li>value - The value being set</li>
36473              * <li>originalValue - The original value for the field, before the edit.</li>
36474              * <li>row - The grid row index</li>
36475              * <li>column - The grid column index</li>
36476              * </ul>
36477              * @param {Object} e An edit event (see above for description)
36478              */
36479             "afteredit" : true,
36480             /**
36481              * @event validateedit
36482              * Fires after a cell is edited, but before the value is set in the record. 
36483          * You can use this to modify the value being set in the field, Return false
36484              * to cancel the change. The edit event object has the following properties <br />
36485              * <ul style="padding:5px;padding-left:16px;">
36486          * <li>editor - This editor</li>
36487              * <li>grid - This grid</li>
36488              * <li>record - The record being edited</li>
36489              * <li>field - The field name being edited</li>
36490              * <li>value - The value being set</li>
36491              * <li>originalValue - The original value for the field, before the edit.</li>
36492              * <li>row - The grid row index</li>
36493              * <li>column - The grid column index</li>
36494              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36495              * </ul>
36496              * @param {Object} e An edit event (see above for description)
36497              */
36498             "validateedit" : true
36499         });
36500     this.on("bodyscroll", this.stopEditing,  this);
36501     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36502 };
36503
36504 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36505     /**
36506      * @cfg {Number} clicksToEdit
36507      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36508      */
36509     clicksToEdit: 2,
36510
36511     // private
36512     isEditor : true,
36513     // private
36514     trackMouseOver: false, // causes very odd FF errors
36515
36516     onCellDblClick : function(g, row, col){
36517         this.startEditing(row, col);
36518     },
36519
36520     onEditComplete : function(ed, value, startValue){
36521         this.editing = false;
36522         this.activeEditor = null;
36523         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36524         var r = ed.record;
36525         var field = this.colModel.getDataIndex(ed.col);
36526         var e = {
36527             grid: this,
36528             record: r,
36529             field: field,
36530             originalValue: startValue,
36531             value: value,
36532             row: ed.row,
36533             column: ed.col,
36534             cancel:false,
36535             editor: ed
36536         };
36537         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36538         cell.show();
36539           
36540         if(String(value) !== String(startValue)){
36541             
36542             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36543                 r.set(field, e.value);
36544                 // if we are dealing with a combo box..
36545                 // then we also set the 'name' colum to be the displayField
36546                 if (ed.field.displayField && ed.field.name) {
36547                     r.set(ed.field.name, ed.field.el.dom.value);
36548                 }
36549                 
36550                 delete e.cancel; //?? why!!!
36551                 this.fireEvent("afteredit", e);
36552             }
36553         } else {
36554             this.fireEvent("afteredit", e); // always fire it!
36555         }
36556         this.view.focusCell(ed.row, ed.col);
36557     },
36558
36559     /**
36560      * Starts editing the specified for the specified row/column
36561      * @param {Number} rowIndex
36562      * @param {Number} colIndex
36563      */
36564     startEditing : function(row, col){
36565         this.stopEditing();
36566         if(this.colModel.isCellEditable(col, row)){
36567             this.view.ensureVisible(row, col, true);
36568           
36569             var r = this.dataSource.getAt(row);
36570             var field = this.colModel.getDataIndex(col);
36571             var cell = Roo.get(this.view.getCell(row,col));
36572             var e = {
36573                 grid: this,
36574                 record: r,
36575                 field: field,
36576                 value: r.data[field],
36577                 row: row,
36578                 column: col,
36579                 cancel:false 
36580             };
36581             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36582                 this.editing = true;
36583                 var ed = this.colModel.getCellEditor(col, row);
36584                 
36585                 if (!ed) {
36586                     return;
36587                 }
36588                 if(!ed.rendered){
36589                     ed.render(ed.parentEl || document.body);
36590                 }
36591                 ed.field.reset();
36592                
36593                 cell.hide();
36594                 
36595                 (function(){ // complex but required for focus issues in safari, ie and opera
36596                     ed.row = row;
36597                     ed.col = col;
36598                     ed.record = r;
36599                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36600                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36601                     this.activeEditor = ed;
36602                     var v = r.data[field];
36603                     ed.startEdit(this.view.getCell(row, col), v);
36604                     // combo's with 'displayField and name set
36605                     if (ed.field.displayField && ed.field.name) {
36606                         ed.field.el.dom.value = r.data[ed.field.name];
36607                     }
36608                     
36609                     
36610                 }).defer(50, this);
36611             }
36612         }
36613     },
36614         
36615     /**
36616      * Stops any active editing
36617      */
36618     stopEditing : function(){
36619         if(this.activeEditor){
36620             this.activeEditor.completeEdit();
36621         }
36622         this.activeEditor = null;
36623     },
36624         
36625          /**
36626      * Called to get grid's drag proxy text, by default returns this.ddText.
36627      * @return {String}
36628      */
36629     getDragDropText : function(){
36630         var count = this.selModel.getSelectedCell() ? 1 : 0;
36631         return String.format(this.ddText, count, count == 1 ? '' : 's');
36632     }
36633         
36634 });/*
36635  * Based on:
36636  * Ext JS Library 1.1.1
36637  * Copyright(c) 2006-2007, Ext JS, LLC.
36638  *
36639  * Originally Released Under LGPL - original licence link has changed is not relivant.
36640  *
36641  * Fork - LGPL
36642  * <script type="text/javascript">
36643  */
36644
36645 // private - not really -- you end up using it !
36646 // This is a support class used internally by the Grid components
36647
36648 /**
36649  * @class Roo.grid.GridEditor
36650  * @extends Roo.Editor
36651  * Class for creating and editable grid elements.
36652  * @param {Object} config any settings (must include field)
36653  */
36654 Roo.grid.GridEditor = function(field, config){
36655     if (!config && field.field) {
36656         config = field;
36657         field = Roo.factory(config.field, Roo.form);
36658     }
36659     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36660     field.monitorTab = false;
36661 };
36662
36663 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36664     
36665     /**
36666      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36667      */
36668     
36669     alignment: "tl-tl",
36670     autoSize: "width",
36671     hideEl : false,
36672     cls: "x-small-editor x-grid-editor",
36673     shim:false,
36674     shadow:"frame"
36675 });/*
36676  * Based on:
36677  * Ext JS Library 1.1.1
36678  * Copyright(c) 2006-2007, Ext JS, LLC.
36679  *
36680  * Originally Released Under LGPL - original licence link has changed is not relivant.
36681  *
36682  * Fork - LGPL
36683  * <script type="text/javascript">
36684  */
36685   
36686
36687   
36688 Roo.grid.PropertyRecord = Roo.data.Record.create([
36689     {name:'name',type:'string'},  'value'
36690 ]);
36691
36692
36693 Roo.grid.PropertyStore = function(grid, source){
36694     this.grid = grid;
36695     this.store = new Roo.data.Store({
36696         recordType : Roo.grid.PropertyRecord
36697     });
36698     this.store.on('update', this.onUpdate,  this);
36699     if(source){
36700         this.setSource(source);
36701     }
36702     Roo.grid.PropertyStore.superclass.constructor.call(this);
36703 };
36704
36705
36706
36707 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36708     setSource : function(o){
36709         this.source = o;
36710         this.store.removeAll();
36711         var data = [];
36712         for(var k in o){
36713             if(this.isEditableValue(o[k])){
36714                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36715             }
36716         }
36717         this.store.loadRecords({records: data}, {}, true);
36718     },
36719
36720     onUpdate : function(ds, record, type){
36721         if(type == Roo.data.Record.EDIT){
36722             var v = record.data['value'];
36723             var oldValue = record.modified['value'];
36724             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36725                 this.source[record.id] = v;
36726                 record.commit();
36727                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36728             }else{
36729                 record.reject();
36730             }
36731         }
36732     },
36733
36734     getProperty : function(row){
36735        return this.store.getAt(row);
36736     },
36737
36738     isEditableValue: function(val){
36739         if(val && val instanceof Date){
36740             return true;
36741         }else if(typeof val == 'object' || typeof val == 'function'){
36742             return false;
36743         }
36744         return true;
36745     },
36746
36747     setValue : function(prop, value){
36748         this.source[prop] = value;
36749         this.store.getById(prop).set('value', value);
36750     },
36751
36752     getSource : function(){
36753         return this.source;
36754     }
36755 });
36756
36757 Roo.grid.PropertyColumnModel = function(grid, store){
36758     this.grid = grid;
36759     var g = Roo.grid;
36760     g.PropertyColumnModel.superclass.constructor.call(this, [
36761         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36762         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36763     ]);
36764     this.store = store;
36765     this.bselect = Roo.DomHelper.append(document.body, {
36766         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36767             {tag: 'option', value: 'true', html: 'true'},
36768             {tag: 'option', value: 'false', html: 'false'}
36769         ]
36770     });
36771     Roo.id(this.bselect);
36772     var f = Roo.form;
36773     this.editors = {
36774         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36775         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36776         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36777         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36778         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36779     };
36780     this.renderCellDelegate = this.renderCell.createDelegate(this);
36781     this.renderPropDelegate = this.renderProp.createDelegate(this);
36782 };
36783
36784 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36785     
36786     
36787     nameText : 'Name',
36788     valueText : 'Value',
36789     
36790     dateFormat : 'm/j/Y',
36791     
36792     
36793     renderDate : function(dateVal){
36794         return dateVal.dateFormat(this.dateFormat);
36795     },
36796
36797     renderBool : function(bVal){
36798         return bVal ? 'true' : 'false';
36799     },
36800
36801     isCellEditable : function(colIndex, rowIndex){
36802         return colIndex == 1;
36803     },
36804
36805     getRenderer : function(col){
36806         return col == 1 ?
36807             this.renderCellDelegate : this.renderPropDelegate;
36808     },
36809
36810     renderProp : function(v){
36811         return this.getPropertyName(v);
36812     },
36813
36814     renderCell : function(val){
36815         var rv = val;
36816         if(val instanceof Date){
36817             rv = this.renderDate(val);
36818         }else if(typeof val == 'boolean'){
36819             rv = this.renderBool(val);
36820         }
36821         return Roo.util.Format.htmlEncode(rv);
36822     },
36823
36824     getPropertyName : function(name){
36825         var pn = this.grid.propertyNames;
36826         return pn && pn[name] ? pn[name] : name;
36827     },
36828
36829     getCellEditor : function(colIndex, rowIndex){
36830         var p = this.store.getProperty(rowIndex);
36831         var n = p.data['name'], val = p.data['value'];
36832         
36833         if(typeof(this.grid.customEditors[n]) == 'string'){
36834             return this.editors[this.grid.customEditors[n]];
36835         }
36836         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36837             return this.grid.customEditors[n];
36838         }
36839         if(val instanceof Date){
36840             return this.editors['date'];
36841         }else if(typeof val == 'number'){
36842             return this.editors['number'];
36843         }else if(typeof val == 'boolean'){
36844             return this.editors['boolean'];
36845         }else{
36846             return this.editors['string'];
36847         }
36848     }
36849 });
36850
36851 /**
36852  * @class Roo.grid.PropertyGrid
36853  * @extends Roo.grid.EditorGrid
36854  * This class represents the  interface of a component based property grid control.
36855  * <br><br>Usage:<pre><code>
36856  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36857       
36858  });
36859  // set any options
36860  grid.render();
36861  * </code></pre>
36862   
36863  * @constructor
36864  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36865  * The container MUST have some type of size defined for the grid to fill. The container will be
36866  * automatically set to position relative if it isn't already.
36867  * @param {Object} config A config object that sets properties on this grid.
36868  */
36869 Roo.grid.PropertyGrid = function(container, config){
36870     config = config || {};
36871     var store = new Roo.grid.PropertyStore(this);
36872     this.store = store;
36873     var cm = new Roo.grid.PropertyColumnModel(this, store);
36874     store.store.sort('name', 'ASC');
36875     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36876         ds: store.store,
36877         cm: cm,
36878         enableColLock:false,
36879         enableColumnMove:false,
36880         stripeRows:false,
36881         trackMouseOver: false,
36882         clicksToEdit:1
36883     }, config));
36884     this.getGridEl().addClass('x-props-grid');
36885     this.lastEditRow = null;
36886     this.on('columnresize', this.onColumnResize, this);
36887     this.addEvents({
36888          /**
36889              * @event beforepropertychange
36890              * Fires before a property changes (return false to stop?)
36891              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36892              * @param {String} id Record Id
36893              * @param {String} newval New Value
36894          * @param {String} oldval Old Value
36895              */
36896         "beforepropertychange": true,
36897         /**
36898              * @event propertychange
36899              * Fires after a property changes
36900              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36901              * @param {String} id Record Id
36902              * @param {String} newval New Value
36903          * @param {String} oldval Old Value
36904              */
36905         "propertychange": true
36906     });
36907     this.customEditors = this.customEditors || {};
36908 };
36909 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36910     
36911      /**
36912      * @cfg {Object} customEditors map of colnames=> custom editors.
36913      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36914      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36915      * false disables editing of the field.
36916          */
36917     
36918       /**
36919      * @cfg {Object} propertyNames map of property Names to their displayed value
36920          */
36921     
36922     render : function(){
36923         Roo.grid.PropertyGrid.superclass.render.call(this);
36924         this.autoSize.defer(100, this);
36925     },
36926
36927     autoSize : function(){
36928         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36929         if(this.view){
36930             this.view.fitColumns();
36931         }
36932     },
36933
36934     onColumnResize : function(){
36935         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36936         this.autoSize();
36937     },
36938     /**
36939      * Sets the data for the Grid
36940      * accepts a Key => Value object of all the elements avaiable.
36941      * @param {Object} data  to appear in grid.
36942      */
36943     setSource : function(source){
36944         this.store.setSource(source);
36945         //this.autoSize();
36946     },
36947     /**
36948      * Gets all the data from the grid.
36949      * @return {Object} data  data stored in grid
36950      */
36951     getSource : function(){
36952         return this.store.getSource();
36953     }
36954 });/*
36955   
36956  * Licence LGPL
36957  
36958  */
36959  
36960 /**
36961  * @class Roo.grid.Calendar
36962  * @extends Roo.util.Grid
36963  * This class extends the Grid to provide a calendar widget
36964  * <br><br>Usage:<pre><code>
36965  var grid = new Roo.grid.Calendar("my-container-id", {
36966      ds: myDataStore,
36967      cm: myColModel,
36968      selModel: mySelectionModel,
36969      autoSizeColumns: true,
36970      monitorWindowResize: false,
36971      trackMouseOver: true
36972      eventstore : real data store..
36973  });
36974  // set any options
36975  grid.render();
36976   
36977   * @constructor
36978  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36979  * The container MUST have some type of size defined for the grid to fill. The container will be
36980  * automatically set to position relative if it isn't already.
36981  * @param {Object} config A config object that sets properties on this grid.
36982  */
36983 Roo.grid.Calendar = function(container, config){
36984         // initialize the container
36985         this.container = Roo.get(container);
36986         this.container.update("");
36987         this.container.setStyle("overflow", "hidden");
36988     this.container.addClass('x-grid-container');
36989
36990     this.id = this.container.id;
36991
36992     Roo.apply(this, config);
36993     // check and correct shorthanded configs
36994     
36995     var rows = [];
36996     var d =1;
36997     for (var r = 0;r < 6;r++) {
36998         
36999         rows[r]=[];
37000         for (var c =0;c < 7;c++) {
37001             rows[r][c]= '';
37002         }
37003     }
37004     if (this.eventStore) {
37005         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37006         this.eventStore.on('load',this.onLoad, this);
37007         this.eventStore.on('beforeload',this.clearEvents, this);
37008          
37009     }
37010     
37011     this.dataSource = new Roo.data.Store({
37012             proxy: new Roo.data.MemoryProxy(rows),
37013             reader: new Roo.data.ArrayReader({}, [
37014                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37015     });
37016
37017     this.dataSource.load();
37018     this.ds = this.dataSource;
37019     this.ds.xmodule = this.xmodule || false;
37020     
37021     
37022     var cellRender = function(v,x,r)
37023     {
37024         return String.format(
37025             '<div class="fc-day  fc-widget-content"><div>' +
37026                 '<div class="fc-event-container"></div>' +
37027                 '<div class="fc-day-number">{0}</div>'+
37028                 
37029                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37030             '</div></div>', v);
37031     
37032     }
37033     
37034     
37035     this.colModel = new Roo.grid.ColumnModel( [
37036         {
37037             xtype: 'ColumnModel',
37038             xns: Roo.grid,
37039             dataIndex : 'weekday0',
37040             header : 'Sunday',
37041             renderer : cellRender
37042         },
37043         {
37044             xtype: 'ColumnModel',
37045             xns: Roo.grid,
37046             dataIndex : 'weekday1',
37047             header : 'Monday',
37048             renderer : cellRender
37049         },
37050         {
37051             xtype: 'ColumnModel',
37052             xns: Roo.grid,
37053             dataIndex : 'weekday2',
37054             header : 'Tuesday',
37055             renderer : cellRender
37056         },
37057         {
37058             xtype: 'ColumnModel',
37059             xns: Roo.grid,
37060             dataIndex : 'weekday3',
37061             header : 'Wednesday',
37062             renderer : cellRender
37063         },
37064         {
37065             xtype: 'ColumnModel',
37066             xns: Roo.grid,
37067             dataIndex : 'weekday4',
37068             header : 'Thursday',
37069             renderer : cellRender
37070         },
37071         {
37072             xtype: 'ColumnModel',
37073             xns: Roo.grid,
37074             dataIndex : 'weekday5',
37075             header : 'Friday',
37076             renderer : cellRender
37077         },
37078         {
37079             xtype: 'ColumnModel',
37080             xns: Roo.grid,
37081             dataIndex : 'weekday6',
37082             header : 'Saturday',
37083             renderer : cellRender
37084         }
37085     ]);
37086     this.cm = this.colModel;
37087     this.cm.xmodule = this.xmodule || false;
37088  
37089         
37090           
37091     //this.selModel = new Roo.grid.CellSelectionModel();
37092     //this.sm = this.selModel;
37093     //this.selModel.init(this);
37094     
37095     
37096     if(this.width){
37097         this.container.setWidth(this.width);
37098     }
37099
37100     if(this.height){
37101         this.container.setHeight(this.height);
37102     }
37103     /** @private */
37104         this.addEvents({
37105         // raw events
37106         /**
37107          * @event click
37108          * The raw click event for the entire grid.
37109          * @param {Roo.EventObject} e
37110          */
37111         "click" : true,
37112         /**
37113          * @event dblclick
37114          * The raw dblclick event for the entire grid.
37115          * @param {Roo.EventObject} e
37116          */
37117         "dblclick" : true,
37118         /**
37119          * @event contextmenu
37120          * The raw contextmenu event for the entire grid.
37121          * @param {Roo.EventObject} e
37122          */
37123         "contextmenu" : true,
37124         /**
37125          * @event mousedown
37126          * The raw mousedown event for the entire grid.
37127          * @param {Roo.EventObject} e
37128          */
37129         "mousedown" : true,
37130         /**
37131          * @event mouseup
37132          * The raw mouseup event for the entire grid.
37133          * @param {Roo.EventObject} e
37134          */
37135         "mouseup" : true,
37136         /**
37137          * @event mouseover
37138          * The raw mouseover event for the entire grid.
37139          * @param {Roo.EventObject} e
37140          */
37141         "mouseover" : true,
37142         /**
37143          * @event mouseout
37144          * The raw mouseout event for the entire grid.
37145          * @param {Roo.EventObject} e
37146          */
37147         "mouseout" : true,
37148         /**
37149          * @event keypress
37150          * The raw keypress event for the entire grid.
37151          * @param {Roo.EventObject} e
37152          */
37153         "keypress" : true,
37154         /**
37155          * @event keydown
37156          * The raw keydown event for the entire grid.
37157          * @param {Roo.EventObject} e
37158          */
37159         "keydown" : true,
37160
37161         // custom events
37162
37163         /**
37164          * @event cellclick
37165          * Fires when a cell is clicked
37166          * @param {Grid} this
37167          * @param {Number} rowIndex
37168          * @param {Number} columnIndex
37169          * @param {Roo.EventObject} e
37170          */
37171         "cellclick" : true,
37172         /**
37173          * @event celldblclick
37174          * Fires when a cell is double clicked
37175          * @param {Grid} this
37176          * @param {Number} rowIndex
37177          * @param {Number} columnIndex
37178          * @param {Roo.EventObject} e
37179          */
37180         "celldblclick" : true,
37181         /**
37182          * @event rowclick
37183          * Fires when a row is clicked
37184          * @param {Grid} this
37185          * @param {Number} rowIndex
37186          * @param {Roo.EventObject} e
37187          */
37188         "rowclick" : true,
37189         /**
37190          * @event rowdblclick
37191          * Fires when a row is double clicked
37192          * @param {Grid} this
37193          * @param {Number} rowIndex
37194          * @param {Roo.EventObject} e
37195          */
37196         "rowdblclick" : true,
37197         /**
37198          * @event headerclick
37199          * Fires when a header is clicked
37200          * @param {Grid} this
37201          * @param {Number} columnIndex
37202          * @param {Roo.EventObject} e
37203          */
37204         "headerclick" : true,
37205         /**
37206          * @event headerdblclick
37207          * Fires when a header cell is double clicked
37208          * @param {Grid} this
37209          * @param {Number} columnIndex
37210          * @param {Roo.EventObject} e
37211          */
37212         "headerdblclick" : true,
37213         /**
37214          * @event rowcontextmenu
37215          * Fires when a row is right clicked
37216          * @param {Grid} this
37217          * @param {Number} rowIndex
37218          * @param {Roo.EventObject} e
37219          */
37220         "rowcontextmenu" : true,
37221         /**
37222          * @event cellcontextmenu
37223          * Fires when a cell is right clicked
37224          * @param {Grid} this
37225          * @param {Number} rowIndex
37226          * @param {Number} cellIndex
37227          * @param {Roo.EventObject} e
37228          */
37229          "cellcontextmenu" : true,
37230         /**
37231          * @event headercontextmenu
37232          * Fires when a header is right clicked
37233          * @param {Grid} this
37234          * @param {Number} columnIndex
37235          * @param {Roo.EventObject} e
37236          */
37237         "headercontextmenu" : true,
37238         /**
37239          * @event bodyscroll
37240          * Fires when the body element is scrolled
37241          * @param {Number} scrollLeft
37242          * @param {Number} scrollTop
37243          */
37244         "bodyscroll" : true,
37245         /**
37246          * @event columnresize
37247          * Fires when the user resizes a column
37248          * @param {Number} columnIndex
37249          * @param {Number} newSize
37250          */
37251         "columnresize" : true,
37252         /**
37253          * @event columnmove
37254          * Fires when the user moves a column
37255          * @param {Number} oldIndex
37256          * @param {Number} newIndex
37257          */
37258         "columnmove" : true,
37259         /**
37260          * @event startdrag
37261          * Fires when row(s) start being dragged
37262          * @param {Grid} this
37263          * @param {Roo.GridDD} dd The drag drop object
37264          * @param {event} e The raw browser event
37265          */
37266         "startdrag" : true,
37267         /**
37268          * @event enddrag
37269          * Fires when a drag operation is complete
37270          * @param {Grid} this
37271          * @param {Roo.GridDD} dd The drag drop object
37272          * @param {event} e The raw browser event
37273          */
37274         "enddrag" : true,
37275         /**
37276          * @event dragdrop
37277          * Fires when dragged row(s) are dropped on a valid DD target
37278          * @param {Grid} this
37279          * @param {Roo.GridDD} dd The drag drop object
37280          * @param {String} targetId The target drag drop object
37281          * @param {event} e The raw browser event
37282          */
37283         "dragdrop" : true,
37284         /**
37285          * @event dragover
37286          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37287          * @param {Grid} this
37288          * @param {Roo.GridDD} dd The drag drop object
37289          * @param {String} targetId The target drag drop object
37290          * @param {event} e The raw browser event
37291          */
37292         "dragover" : true,
37293         /**
37294          * @event dragenter
37295          *  Fires when the dragged row(s) first cross another DD target while being dragged
37296          * @param {Grid} this
37297          * @param {Roo.GridDD} dd The drag drop object
37298          * @param {String} targetId The target drag drop object
37299          * @param {event} e The raw browser event
37300          */
37301         "dragenter" : true,
37302         /**
37303          * @event dragout
37304          * Fires when the dragged row(s) leave another DD target while being dragged
37305          * @param {Grid} this
37306          * @param {Roo.GridDD} dd The drag drop object
37307          * @param {String} targetId The target drag drop object
37308          * @param {event} e The raw browser event
37309          */
37310         "dragout" : true,
37311         /**
37312          * @event rowclass
37313          * Fires when a row is rendered, so you can change add a style to it.
37314          * @param {GridView} gridview   The grid view
37315          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37316          */
37317         'rowclass' : true,
37318
37319         /**
37320          * @event render
37321          * Fires when the grid is rendered
37322          * @param {Grid} grid
37323          */
37324         'render' : true,
37325             /**
37326              * @event select
37327              * Fires when a date is selected
37328              * @param {DatePicker} this
37329              * @param {Date} date The selected date
37330              */
37331         'select': true,
37332         /**
37333              * @event monthchange
37334              * Fires when the displayed month changes 
37335              * @param {DatePicker} this
37336              * @param {Date} date The selected month
37337              */
37338         'monthchange': true,
37339         /**
37340              * @event evententer
37341              * Fires when mouse over an event
37342              * @param {Calendar} this
37343              * @param {event} Event
37344              */
37345         'evententer': true,
37346         /**
37347              * @event eventleave
37348              * Fires when the mouse leaves an
37349              * @param {Calendar} this
37350              * @param {event}
37351              */
37352         'eventleave': true,
37353         /**
37354              * @event eventclick
37355              * Fires when the mouse click an
37356              * @param {Calendar} this
37357              * @param {event}
37358              */
37359         'eventclick': true,
37360         /**
37361              * @event eventrender
37362              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37363              * @param {Calendar} this
37364              * @param {data} data to be modified
37365              */
37366         'eventrender': true
37367         
37368     });
37369
37370     Roo.grid.Grid.superclass.constructor.call(this);
37371     this.on('render', function() {
37372         this.view.el.addClass('x-grid-cal'); 
37373         
37374         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37375
37376     },this);
37377     
37378     if (!Roo.grid.Calendar.style) {
37379         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37380             
37381             
37382             '.x-grid-cal .x-grid-col' :  {
37383                 height: 'auto !important',
37384                 'vertical-align': 'top'
37385             },
37386             '.x-grid-cal  .fc-event-hori' : {
37387                 height: '14px'
37388             }
37389              
37390             
37391         }, Roo.id());
37392     }
37393
37394     
37395     
37396 };
37397 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37398     /**
37399      * @cfg {Store} eventStore The store that loads events.
37400      */
37401     eventStore : 25,
37402
37403      
37404     activeDate : false,
37405     startDay : 0,
37406     autoWidth : true,
37407     monitorWindowResize : false,
37408
37409     
37410     resizeColumns : function() {
37411         var col = (this.view.el.getWidth() / 7) - 3;
37412         // loop through cols, and setWidth
37413         for(var i =0 ; i < 7 ; i++){
37414             this.cm.setColumnWidth(i, col);
37415         }
37416     },
37417      setDate :function(date) {
37418         
37419         Roo.log('setDate?');
37420         
37421         this.resizeColumns();
37422         var vd = this.activeDate;
37423         this.activeDate = date;
37424 //        if(vd && this.el){
37425 //            var t = date.getTime();
37426 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37427 //                Roo.log('using add remove');
37428 //                
37429 //                this.fireEvent('monthchange', this, date);
37430 //                
37431 //                this.cells.removeClass("fc-state-highlight");
37432 //                this.cells.each(function(c){
37433 //                   if(c.dateValue == t){
37434 //                       c.addClass("fc-state-highlight");
37435 //                       setTimeout(function(){
37436 //                            try{c.dom.firstChild.focus();}catch(e){}
37437 //                       }, 50);
37438 //                       return false;
37439 //                   }
37440 //                   return true;
37441 //                });
37442 //                return;
37443 //            }
37444 //        }
37445         
37446         var days = date.getDaysInMonth();
37447         
37448         var firstOfMonth = date.getFirstDateOfMonth();
37449         var startingPos = firstOfMonth.getDay()-this.startDay;
37450         
37451         if(startingPos < this.startDay){
37452             startingPos += 7;
37453         }
37454         
37455         var pm = date.add(Date.MONTH, -1);
37456         var prevStart = pm.getDaysInMonth()-startingPos;
37457 //        
37458         
37459         
37460         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37461         
37462         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37463         //this.cells.addClassOnOver('fc-state-hover');
37464         
37465         var cells = this.cells.elements;
37466         var textEls = this.textNodes;
37467         
37468         //Roo.each(cells, function(cell){
37469         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37470         //});
37471         
37472         days += startingPos;
37473
37474         // convert everything to numbers so it's fast
37475         var day = 86400000;
37476         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37477         //Roo.log(d);
37478         //Roo.log(pm);
37479         //Roo.log(prevStart);
37480         
37481         var today = new Date().clearTime().getTime();
37482         var sel = date.clearTime().getTime();
37483         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37484         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37485         var ddMatch = this.disabledDatesRE;
37486         var ddText = this.disabledDatesText;
37487         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37488         var ddaysText = this.disabledDaysText;
37489         var format = this.format;
37490         
37491         var setCellClass = function(cal, cell){
37492             
37493             //Roo.log('set Cell Class');
37494             cell.title = "";
37495             var t = d.getTime();
37496             
37497             //Roo.log(d);
37498             
37499             
37500             cell.dateValue = t;
37501             if(t == today){
37502                 cell.className += " fc-today";
37503                 cell.className += " fc-state-highlight";
37504                 cell.title = cal.todayText;
37505             }
37506             if(t == sel){
37507                 // disable highlight in other month..
37508                 cell.className += " fc-state-highlight";
37509                 
37510             }
37511             // disabling
37512             if(t < min) {
37513                 //cell.className = " fc-state-disabled";
37514                 cell.title = cal.minText;
37515                 return;
37516             }
37517             if(t > max) {
37518                 //cell.className = " fc-state-disabled";
37519                 cell.title = cal.maxText;
37520                 return;
37521             }
37522             if(ddays){
37523                 if(ddays.indexOf(d.getDay()) != -1){
37524                     // cell.title = ddaysText;
37525                    // cell.className = " fc-state-disabled";
37526                 }
37527             }
37528             if(ddMatch && format){
37529                 var fvalue = d.dateFormat(format);
37530                 if(ddMatch.test(fvalue)){
37531                     cell.title = ddText.replace("%0", fvalue);
37532                    cell.className = " fc-state-disabled";
37533                 }
37534             }
37535             
37536             if (!cell.initialClassName) {
37537                 cell.initialClassName = cell.dom.className;
37538             }
37539             
37540             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37541         };
37542
37543         var i = 0;
37544         
37545         for(; i < startingPos; i++) {
37546             cells[i].dayName =  (++prevStart);
37547             Roo.log(textEls[i]);
37548             d.setDate(d.getDate()+1);
37549             
37550             //cells[i].className = "fc-past fc-other-month";
37551             setCellClass(this, cells[i]);
37552         }
37553         
37554         var intDay = 0;
37555         
37556         for(; i < days; i++){
37557             intDay = i - startingPos + 1;
37558             cells[i].dayName =  (intDay);
37559             d.setDate(d.getDate()+1);
37560             
37561             cells[i].className = ''; // "x-date-active";
37562             setCellClass(this, cells[i]);
37563         }
37564         var extraDays = 0;
37565         
37566         for(; i < 42; i++) {
37567             //textEls[i].innerHTML = (++extraDays);
37568             
37569             d.setDate(d.getDate()+1);
37570             cells[i].dayName = (++extraDays);
37571             cells[i].className = "fc-future fc-other-month";
37572             setCellClass(this, cells[i]);
37573         }
37574         
37575         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37576         
37577         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37578         
37579         // this will cause all the cells to mis
37580         var rows= [];
37581         var i =0;
37582         for (var r = 0;r < 6;r++) {
37583             for (var c =0;c < 7;c++) {
37584                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37585             }    
37586         }
37587         
37588         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37589         for(i=0;i<cells.length;i++) {
37590             
37591             this.cells.elements[i].dayName = cells[i].dayName ;
37592             this.cells.elements[i].className = cells[i].className;
37593             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37594             this.cells.elements[i].title = cells[i].title ;
37595             this.cells.elements[i].dateValue = cells[i].dateValue ;
37596         }
37597         
37598         
37599         
37600         
37601         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37602         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37603         
37604         ////if(totalRows != 6){
37605             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37606            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37607        // }
37608         
37609         this.fireEvent('monthchange', this, date);
37610         
37611         
37612     },
37613  /**
37614      * Returns the grid's SelectionModel.
37615      * @return {SelectionModel}
37616      */
37617     getSelectionModel : function(){
37618         if(!this.selModel){
37619             this.selModel = new Roo.grid.CellSelectionModel();
37620         }
37621         return this.selModel;
37622     },
37623
37624     load: function() {
37625         this.eventStore.load()
37626         
37627         
37628         
37629     },
37630     
37631     findCell : function(dt) {
37632         dt = dt.clearTime().getTime();
37633         var ret = false;
37634         this.cells.each(function(c){
37635             //Roo.log("check " +c.dateValue + '?=' + dt);
37636             if(c.dateValue == dt){
37637                 ret = c;
37638                 return false;
37639             }
37640             return true;
37641         });
37642         
37643         return ret;
37644     },
37645     
37646     findCells : function(rec) {
37647         var s = rec.data.start_dt.clone().clearTime().getTime();
37648        // Roo.log(s);
37649         var e= rec.data.end_dt.clone().clearTime().getTime();
37650        // Roo.log(e);
37651         var ret = [];
37652         this.cells.each(function(c){
37653              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37654             
37655             if(c.dateValue > e){
37656                 return ;
37657             }
37658             if(c.dateValue < s){
37659                 return ;
37660             }
37661             ret.push(c);
37662         });
37663         
37664         return ret;    
37665     },
37666     
37667     findBestRow: function(cells)
37668     {
37669         var ret = 0;
37670         
37671         for (var i =0 ; i < cells.length;i++) {
37672             ret  = Math.max(cells[i].rows || 0,ret);
37673         }
37674         return ret;
37675         
37676     },
37677     
37678     
37679     addItem : function(rec)
37680     {
37681         // look for vertical location slot in
37682         var cells = this.findCells(rec);
37683         
37684         rec.row = this.findBestRow(cells);
37685         
37686         // work out the location.
37687         
37688         var crow = false;
37689         var rows = [];
37690         for(var i =0; i < cells.length; i++) {
37691             if (!crow) {
37692                 crow = {
37693                     start : cells[i],
37694                     end :  cells[i]
37695                 };
37696                 continue;
37697             }
37698             if (crow.start.getY() == cells[i].getY()) {
37699                 // on same row.
37700                 crow.end = cells[i];
37701                 continue;
37702             }
37703             // different row.
37704             rows.push(crow);
37705             crow = {
37706                 start: cells[i],
37707                 end : cells[i]
37708             };
37709             
37710         }
37711         
37712         rows.push(crow);
37713         rec.els = [];
37714         rec.rows = rows;
37715         rec.cells = cells;
37716         for (var i = 0; i < cells.length;i++) {
37717             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37718             
37719         }
37720         
37721         
37722     },
37723     
37724     clearEvents: function() {
37725         
37726         if (!this.eventStore.getCount()) {
37727             return;
37728         }
37729         // reset number of rows in cells.
37730         Roo.each(this.cells.elements, function(c){
37731             c.rows = 0;
37732         });
37733         
37734         this.eventStore.each(function(e) {
37735             this.clearEvent(e);
37736         },this);
37737         
37738     },
37739     
37740     clearEvent : function(ev)
37741     {
37742         if (ev.els) {
37743             Roo.each(ev.els, function(el) {
37744                 el.un('mouseenter' ,this.onEventEnter, this);
37745                 el.un('mouseleave' ,this.onEventLeave, this);
37746                 el.remove();
37747             },this);
37748             ev.els = [];
37749         }
37750     },
37751     
37752     
37753     renderEvent : function(ev,ctr) {
37754         if (!ctr) {
37755              ctr = this.view.el.select('.fc-event-container',true).first();
37756         }
37757         
37758          
37759         this.clearEvent(ev);
37760             //code
37761        
37762         
37763         
37764         ev.els = [];
37765         var cells = ev.cells;
37766         var rows = ev.rows;
37767         this.fireEvent('eventrender', this, ev);
37768         
37769         for(var i =0; i < rows.length; i++) {
37770             
37771             cls = '';
37772             if (i == 0) {
37773                 cls += ' fc-event-start';
37774             }
37775             if ((i+1) == rows.length) {
37776                 cls += ' fc-event-end';
37777             }
37778             
37779             //Roo.log(ev.data);
37780             // how many rows should it span..
37781             var cg = this.eventTmpl.append(ctr,Roo.apply({
37782                 fccls : cls
37783                 
37784             }, ev.data) , true);
37785             
37786             
37787             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37788             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37789             cg.on('click', this.onEventClick, this, ev);
37790             
37791             ev.els.push(cg);
37792             
37793             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37794             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37795             //Roo.log(cg);
37796              
37797             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37798             cg.setWidth(ebox.right - sbox.x -2);
37799         }
37800     },
37801     
37802     renderEvents: function()
37803     {   
37804         // first make sure there is enough space..
37805         
37806         if (!this.eventTmpl) {
37807             this.eventTmpl = new Roo.Template(
37808                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37809                     '<div class="fc-event-inner">' +
37810                         '<span class="fc-event-time">{time}</span>' +
37811                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37812                     '</div>' +
37813                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37814                 '</div>'
37815             );
37816                 
37817         }
37818                
37819         
37820         
37821         this.cells.each(function(c) {
37822             //Roo.log(c.select('.fc-day-content div',true).first());
37823             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37824         });
37825         
37826         var ctr = this.view.el.select('.fc-event-container',true).first();
37827         
37828         var cls;
37829         this.eventStore.each(function(ev){
37830             
37831             this.renderEvent(ev);
37832              
37833              
37834         }, this);
37835         this.view.layout();
37836         
37837     },
37838     
37839     onEventEnter: function (e, el,event,d) {
37840         this.fireEvent('evententer', this, el, event);
37841     },
37842     
37843     onEventLeave: function (e, el,event,d) {
37844         this.fireEvent('eventleave', this, el, event);
37845     },
37846     
37847     onEventClick: function (e, el,event,d) {
37848         this.fireEvent('eventclick', this, el, event);
37849     },
37850     
37851     onMonthChange: function () {
37852         this.store.load();
37853     },
37854     
37855     onLoad: function () {
37856         
37857         //Roo.log('calendar onload');
37858 //         
37859         if(this.eventStore.getCount() > 0){
37860             
37861            
37862             
37863             this.eventStore.each(function(d){
37864                 
37865                 
37866                 // FIXME..
37867                 var add =   d.data;
37868                 if (typeof(add.end_dt) == 'undefined')  {
37869                     Roo.log("Missing End time in calendar data: ");
37870                     Roo.log(d);
37871                     return;
37872                 }
37873                 if (typeof(add.start_dt) == 'undefined')  {
37874                     Roo.log("Missing Start time in calendar data: ");
37875                     Roo.log(d);
37876                     return;
37877                 }
37878                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37879                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37880                 add.id = add.id || d.id;
37881                 add.title = add.title || '??';
37882                 
37883                 this.addItem(d);
37884                 
37885              
37886             },this);
37887         }
37888         
37889         this.renderEvents();
37890     }
37891     
37892
37893 });
37894 /*
37895  grid : {
37896                 xtype: 'Grid',
37897                 xns: Roo.grid,
37898                 listeners : {
37899                     render : function ()
37900                     {
37901                         _this.grid = this;
37902                         
37903                         if (!this.view.el.hasClass('course-timesheet')) {
37904                             this.view.el.addClass('course-timesheet');
37905                         }
37906                         if (this.tsStyle) {
37907                             this.ds.load({});
37908                             return; 
37909                         }
37910                         Roo.log('width');
37911                         Roo.log(_this.grid.view.el.getWidth());
37912                         
37913                         
37914                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37915                             '.course-timesheet .x-grid-row' : {
37916                                 height: '80px'
37917                             },
37918                             '.x-grid-row td' : {
37919                                 'vertical-align' : 0
37920                             },
37921                             '.course-edit-link' : {
37922                                 'color' : 'blue',
37923                                 'text-overflow' : 'ellipsis',
37924                                 'overflow' : 'hidden',
37925                                 'white-space' : 'nowrap',
37926                                 'cursor' : 'pointer'
37927                             },
37928                             '.sub-link' : {
37929                                 'color' : 'green'
37930                             },
37931                             '.de-act-sup-link' : {
37932                                 'color' : 'purple',
37933                                 'text-decoration' : 'line-through'
37934                             },
37935                             '.de-act-link' : {
37936                                 'color' : 'red',
37937                                 'text-decoration' : 'line-through'
37938                             },
37939                             '.course-timesheet .course-highlight' : {
37940                                 'border-top-style': 'dashed !important',
37941                                 'border-bottom-bottom': 'dashed !important'
37942                             },
37943                             '.course-timesheet .course-item' : {
37944                                 'font-family'   : 'tahoma, arial, helvetica',
37945                                 'font-size'     : '11px',
37946                                 'overflow'      : 'hidden',
37947                                 'padding-left'  : '10px',
37948                                 'padding-right' : '10px',
37949                                 'padding-top' : '10px' 
37950                             }
37951                             
37952                         }, Roo.id());
37953                                 this.ds.load({});
37954                     }
37955                 },
37956                 autoWidth : true,
37957                 monitorWindowResize : false,
37958                 cellrenderer : function(v,x,r)
37959                 {
37960                     return v;
37961                 },
37962                 sm : {
37963                     xtype: 'CellSelectionModel',
37964                     xns: Roo.grid
37965                 },
37966                 dataSource : {
37967                     xtype: 'Store',
37968                     xns: Roo.data,
37969                     listeners : {
37970                         beforeload : function (_self, options)
37971                         {
37972                             options.params = options.params || {};
37973                             options.params._month = _this.monthField.getValue();
37974                             options.params.limit = 9999;
37975                             options.params['sort'] = 'when_dt';    
37976                             options.params['dir'] = 'ASC';    
37977                             this.proxy.loadResponse = this.loadResponse;
37978                             Roo.log("load?");
37979                             //this.addColumns();
37980                         },
37981                         load : function (_self, records, options)
37982                         {
37983                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37984                                 // if you click on the translation.. you can edit it...
37985                                 var el = Roo.get(this);
37986                                 var id = el.dom.getAttribute('data-id');
37987                                 var d = el.dom.getAttribute('data-date');
37988                                 var t = el.dom.getAttribute('data-time');
37989                                 //var id = this.child('span').dom.textContent;
37990                                 
37991                                 //Roo.log(this);
37992                                 Pman.Dialog.CourseCalendar.show({
37993                                     id : id,
37994                                     when_d : d,
37995                                     when_t : t,
37996                                     productitem_active : id ? 1 : 0
37997                                 }, function() {
37998                                     _this.grid.ds.load({});
37999                                 });
38000                            
38001                            });
38002                            
38003                            _this.panel.fireEvent('resize', [ '', '' ]);
38004                         }
38005                     },
38006                     loadResponse : function(o, success, response){
38007                             // this is overridden on before load..
38008                             
38009                             Roo.log("our code?");       
38010                             //Roo.log(success);
38011                             //Roo.log(response)
38012                             delete this.activeRequest;
38013                             if(!success){
38014                                 this.fireEvent("loadexception", this, o, response);
38015                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38016                                 return;
38017                             }
38018                             var result;
38019                             try {
38020                                 result = o.reader.read(response);
38021                             }catch(e){
38022                                 Roo.log("load exception?");
38023                                 this.fireEvent("loadexception", this, o, response, e);
38024                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38025                                 return;
38026                             }
38027                             Roo.log("ready...");        
38028                             // loop through result.records;
38029                             // and set this.tdate[date] = [] << array of records..
38030                             _this.tdata  = {};
38031                             Roo.each(result.records, function(r){
38032                                 //Roo.log(r.data);
38033                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38034                                     _this.tdata[r.data.when_dt.format('j')] = [];
38035                                 }
38036                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38037                             });
38038                             
38039                             //Roo.log(_this.tdata);
38040                             
38041                             result.records = [];
38042                             result.totalRecords = 6;
38043                     
38044                             // let's generate some duumy records for the rows.
38045                             //var st = _this.dateField.getValue();
38046                             
38047                             // work out monday..
38048                             //st = st.add(Date.DAY, -1 * st.format('w'));
38049                             
38050                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38051                             
38052                             var firstOfMonth = date.getFirstDayOfMonth();
38053                             var days = date.getDaysInMonth();
38054                             var d = 1;
38055                             var firstAdded = false;
38056                             for (var i = 0; i < result.totalRecords ; i++) {
38057                                 //var d= st.add(Date.DAY, i);
38058                                 var row = {};
38059                                 var added = 0;
38060                                 for(var w = 0 ; w < 7 ; w++){
38061                                     if(!firstAdded && firstOfMonth != w){
38062                                         continue;
38063                                     }
38064                                     if(d > days){
38065                                         continue;
38066                                     }
38067                                     firstAdded = true;
38068                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38069                                     row['weekday'+w] = String.format(
38070                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38071                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38072                                                     d,
38073                                                     date.format('Y-m-')+dd
38074                                                 );
38075                                     added++;
38076                                     if(typeof(_this.tdata[d]) != 'undefined'){
38077                                         Roo.each(_this.tdata[d], function(r){
38078                                             var is_sub = '';
38079                                             var deactive = '';
38080                                             var id = r.id;
38081                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38082                                             if(r.parent_id*1>0){
38083                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38084                                                 id = r.parent_id;
38085                                             }
38086                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38087                                                 deactive = 'de-act-link';
38088                                             }
38089                                             
38090                                             row['weekday'+w] += String.format(
38091                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38092                                                     id, //0
38093                                                     r.product_id_name, //1
38094                                                     r.when_dt.format('h:ia'), //2
38095                                                     is_sub, //3
38096                                                     deactive, //4
38097                                                     desc // 5
38098                                             );
38099                                         });
38100                                     }
38101                                     d++;
38102                                 }
38103                                 
38104                                 // only do this if something added..
38105                                 if(added > 0){ 
38106                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38107                                 }
38108                                 
38109                                 
38110                                 // push it twice. (second one with an hour..
38111                                 
38112                             }
38113                             //Roo.log(result);
38114                             this.fireEvent("load", this, o, o.request.arg);
38115                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38116                         },
38117                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38118                     proxy : {
38119                         xtype: 'HttpProxy',
38120                         xns: Roo.data,
38121                         method : 'GET',
38122                         url : baseURL + '/Roo/Shop_course.php'
38123                     },
38124                     reader : {
38125                         xtype: 'JsonReader',
38126                         xns: Roo.data,
38127                         id : 'id',
38128                         fields : [
38129                             {
38130                                 'name': 'id',
38131                                 'type': 'int'
38132                             },
38133                             {
38134                                 'name': 'when_dt',
38135                                 'type': 'string'
38136                             },
38137                             {
38138                                 'name': 'end_dt',
38139                                 'type': 'string'
38140                             },
38141                             {
38142                                 'name': 'parent_id',
38143                                 'type': 'int'
38144                             },
38145                             {
38146                                 'name': 'product_id',
38147                                 'type': 'int'
38148                             },
38149                             {
38150                                 'name': 'productitem_id',
38151                                 'type': 'int'
38152                             },
38153                             {
38154                                 'name': 'guid',
38155                                 'type': 'int'
38156                             }
38157                         ]
38158                     }
38159                 },
38160                 toolbar : {
38161                     xtype: 'Toolbar',
38162                     xns: Roo,
38163                     items : [
38164                         {
38165                             xtype: 'Button',
38166                             xns: Roo.Toolbar,
38167                             listeners : {
38168                                 click : function (_self, e)
38169                                 {
38170                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38171                                     sd.setMonth(sd.getMonth()-1);
38172                                     _this.monthField.setValue(sd.format('Y-m-d'));
38173                                     _this.grid.ds.load({});
38174                                 }
38175                             },
38176                             text : "Back"
38177                         },
38178                         {
38179                             xtype: 'Separator',
38180                             xns: Roo.Toolbar
38181                         },
38182                         {
38183                             xtype: 'MonthField',
38184                             xns: Roo.form,
38185                             listeners : {
38186                                 render : function (_self)
38187                                 {
38188                                     _this.monthField = _self;
38189                                    // _this.monthField.set  today
38190                                 },
38191                                 select : function (combo, date)
38192                                 {
38193                                     _this.grid.ds.load({});
38194                                 }
38195                             },
38196                             value : (function() { return new Date(); })()
38197                         },
38198                         {
38199                             xtype: 'Separator',
38200                             xns: Roo.Toolbar
38201                         },
38202                         {
38203                             xtype: 'TextItem',
38204                             xns: Roo.Toolbar,
38205                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38206                         },
38207                         {
38208                             xtype: 'Fill',
38209                             xns: Roo.Toolbar
38210                         },
38211                         {
38212                             xtype: 'Button',
38213                             xns: Roo.Toolbar,
38214                             listeners : {
38215                                 click : function (_self, e)
38216                                 {
38217                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38218                                     sd.setMonth(sd.getMonth()+1);
38219                                     _this.monthField.setValue(sd.format('Y-m-d'));
38220                                     _this.grid.ds.load({});
38221                                 }
38222                             },
38223                             text : "Next"
38224                         }
38225                     ]
38226                 },
38227                  
38228             }
38229         };
38230         
38231         *//*
38232  * Based on:
38233  * Ext JS Library 1.1.1
38234  * Copyright(c) 2006-2007, Ext JS, LLC.
38235  *
38236  * Originally Released Under LGPL - original licence link has changed is not relivant.
38237  *
38238  * Fork - LGPL
38239  * <script type="text/javascript">
38240  */
38241  
38242 /**
38243  * @class Roo.LoadMask
38244  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38245  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38246  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38247  * element's UpdateManager load indicator and will be destroyed after the initial load.
38248  * @constructor
38249  * Create a new LoadMask
38250  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38251  * @param {Object} config The config object
38252  */
38253 Roo.LoadMask = function(el, config){
38254     this.el = Roo.get(el);
38255     Roo.apply(this, config);
38256     if(this.store){
38257         this.store.on('beforeload', this.onBeforeLoad, this);
38258         this.store.on('load', this.onLoad, this);
38259         this.store.on('loadexception', this.onLoadException, this);
38260         this.removeMask = false;
38261     }else{
38262         var um = this.el.getUpdateManager();
38263         um.showLoadIndicator = false; // disable the default indicator
38264         um.on('beforeupdate', this.onBeforeLoad, this);
38265         um.on('update', this.onLoad, this);
38266         um.on('failure', this.onLoad, this);
38267         this.removeMask = true;
38268     }
38269 };
38270
38271 Roo.LoadMask.prototype = {
38272     /**
38273      * @cfg {Boolean} removeMask
38274      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38275      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38276      */
38277     /**
38278      * @cfg {String} msg
38279      * The text to display in a centered loading message box (defaults to 'Loading...')
38280      */
38281     msg : 'Loading...',
38282     /**
38283      * @cfg {String} msgCls
38284      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38285      */
38286     msgCls : 'x-mask-loading',
38287
38288     /**
38289      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38290      * @type Boolean
38291      */
38292     disabled: false,
38293
38294     /**
38295      * Disables the mask to prevent it from being displayed
38296      */
38297     disable : function(){
38298        this.disabled = true;
38299     },
38300
38301     /**
38302      * Enables the mask so that it can be displayed
38303      */
38304     enable : function(){
38305         this.disabled = false;
38306     },
38307     
38308     onLoadException : function()
38309     {
38310         Roo.log(arguments);
38311         
38312         if (typeof(arguments[3]) != 'undefined') {
38313             Roo.MessageBox.alert("Error loading",arguments[3]);
38314         } 
38315         /*
38316         try {
38317             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38318                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38319             }   
38320         } catch(e) {
38321             
38322         }
38323         */
38324     
38325         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38326     },
38327     // private
38328     onLoad : function()
38329     {
38330         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38331     },
38332
38333     // private
38334     onBeforeLoad : function(){
38335         if(!this.disabled){
38336             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38337         }
38338     },
38339
38340     // private
38341     destroy : function(){
38342         if(this.store){
38343             this.store.un('beforeload', this.onBeforeLoad, this);
38344             this.store.un('load', this.onLoad, this);
38345             this.store.un('loadexception', this.onLoadException, this);
38346         }else{
38347             var um = this.el.getUpdateManager();
38348             um.un('beforeupdate', this.onBeforeLoad, this);
38349             um.un('update', this.onLoad, this);
38350             um.un('failure', this.onLoad, this);
38351         }
38352     }
38353 };/*
38354  * Based on:
38355  * Ext JS Library 1.1.1
38356  * Copyright(c) 2006-2007, Ext JS, LLC.
38357  *
38358  * Originally Released Under LGPL - original licence link has changed is not relivant.
38359  *
38360  * Fork - LGPL
38361  * <script type="text/javascript">
38362  */
38363
38364
38365 /**
38366  * @class Roo.XTemplate
38367  * @extends Roo.Template
38368  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38369 <pre><code>
38370 var t = new Roo.XTemplate(
38371         '&lt;select name="{name}"&gt;',
38372                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38373         '&lt;/select&gt;'
38374 );
38375  
38376 // then append, applying the master template values
38377  </code></pre>
38378  *
38379  * Supported features:
38380  *
38381  *  Tags:
38382
38383 <pre><code>
38384       {a_variable} - output encoded.
38385       {a_variable.format:("Y-m-d")} - call a method on the variable
38386       {a_variable:raw} - unencoded output
38387       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38388       {a_variable:this.method_on_template(...)} - call a method on the template object.
38389  
38390 </code></pre>
38391  *  The tpl tag:
38392 <pre><code>
38393         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38394         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38395         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38396         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38397   
38398         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38399         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38400 </code></pre>
38401  *      
38402  */
38403 Roo.XTemplate = function()
38404 {
38405     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38406     if (this.html) {
38407         this.compile();
38408     }
38409 };
38410
38411
38412 Roo.extend(Roo.XTemplate, Roo.Template, {
38413
38414     /**
38415      * The various sub templates
38416      */
38417     tpls : false,
38418     /**
38419      *
38420      * basic tag replacing syntax
38421      * WORD:WORD()
38422      *
38423      * // you can fake an object call by doing this
38424      *  x.t:(test,tesT) 
38425      * 
38426      */
38427     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38428
38429     /**
38430      * compile the template
38431      *
38432      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38433      *
38434      */
38435     compile: function()
38436     {
38437         var s = this.html;
38438      
38439         s = ['<tpl>', s, '</tpl>'].join('');
38440     
38441         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38442             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38443             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38444             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38445             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38446             m,
38447             id     = 0,
38448             tpls   = [];
38449     
38450         while(true == !!(m = s.match(re))){
38451             var forMatch   = m[0].match(nameRe),
38452                 ifMatch   = m[0].match(ifRe),
38453                 execMatch   = m[0].match(execRe),
38454                 namedMatch   = m[0].match(namedRe),
38455                 
38456                 exp  = null, 
38457                 fn   = null,
38458                 exec = null,
38459                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38460                 
38461             if (ifMatch) {
38462                 // if - puts fn into test..
38463                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38464                 if(exp){
38465                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38466                 }
38467             }
38468             
38469             if (execMatch) {
38470                 // exec - calls a function... returns empty if true is  returned.
38471                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38472                 if(exp){
38473                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38474                 }
38475             }
38476             
38477             
38478             if (name) {
38479                 // for = 
38480                 switch(name){
38481                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38482                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38483                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38484                 }
38485             }
38486             var uid = namedMatch ? namedMatch[1] : id;
38487             
38488             
38489             tpls.push({
38490                 id:     namedMatch ? namedMatch[1] : id,
38491                 target: name,
38492                 exec:   exec,
38493                 test:   fn,
38494                 body:   m[1] || ''
38495             });
38496             if (namedMatch) {
38497                 s = s.replace(m[0], '');
38498             } else { 
38499                 s = s.replace(m[0], '{xtpl'+ id + '}');
38500             }
38501             ++id;
38502         }
38503         this.tpls = [];
38504         for(var i = tpls.length-1; i >= 0; --i){
38505             this.compileTpl(tpls[i]);
38506             this.tpls[tpls[i].id] = tpls[i];
38507         }
38508         this.master = tpls[tpls.length-1];
38509         return this;
38510     },
38511     /**
38512      * same as applyTemplate, except it's done to one of the subTemplates
38513      * when using named templates, you can do:
38514      *
38515      * var str = pl.applySubTemplate('your-name', values);
38516      *
38517      * 
38518      * @param {Number} id of the template
38519      * @param {Object} values to apply to template
38520      * @param {Object} parent (normaly the instance of this object)
38521      */
38522     applySubTemplate : function(id, values, parent)
38523     {
38524         
38525         
38526         var t = this.tpls[id];
38527         
38528         
38529         try { 
38530             if(t.test && !t.test.call(this, values, parent)){
38531                 return '';
38532             }
38533         } catch(e) {
38534             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38535             Roo.log(e.toString());
38536             Roo.log(t.test);
38537             return ''
38538         }
38539         try { 
38540             
38541             if(t.exec && t.exec.call(this, values, parent)){
38542                 return '';
38543             }
38544         } catch(e) {
38545             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38546             Roo.log(e.toString());
38547             Roo.log(t.exec);
38548             return ''
38549         }
38550         try {
38551             var vs = t.target ? t.target.call(this, values, parent) : values;
38552             parent = t.target ? values : parent;
38553             if(t.target && vs instanceof Array){
38554                 var buf = [];
38555                 for(var i = 0, len = vs.length; i < len; i++){
38556                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38557                 }
38558                 return buf.join('');
38559             }
38560             return t.compiled.call(this, vs, parent);
38561         } catch (e) {
38562             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38563             Roo.log(e.toString());
38564             Roo.log(t.compiled);
38565             return '';
38566         }
38567     },
38568
38569     compileTpl : function(tpl)
38570     {
38571         var fm = Roo.util.Format;
38572         var useF = this.disableFormats !== true;
38573         var sep = Roo.isGecko ? "+" : ",";
38574         var undef = function(str) {
38575             Roo.log("Property not found :"  + str);
38576             return '';
38577         };
38578         
38579         var fn = function(m, name, format, args)
38580         {
38581             //Roo.log(arguments);
38582             args = args ? args.replace(/\\'/g,"'") : args;
38583             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38584             if (typeof(format) == 'undefined') {
38585                 format= 'htmlEncode';
38586             }
38587             if (format == 'raw' ) {
38588                 format = false;
38589             }
38590             
38591             if(name.substr(0, 4) == 'xtpl'){
38592                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38593             }
38594             
38595             // build an array of options to determine if value is undefined..
38596             
38597             // basically get 'xxxx.yyyy' then do
38598             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38599             //    (function () { Roo.log("Property not found"); return ''; })() :
38600             //    ......
38601             
38602             var udef_ar = [];
38603             var lookfor = '';
38604             Roo.each(name.split('.'), function(st) {
38605                 lookfor += (lookfor.length ? '.': '') + st;
38606                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38607             });
38608             
38609             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38610             
38611             
38612             if(format && useF){
38613                 
38614                 args = args ? ',' + args : "";
38615                  
38616                 if(format.substr(0, 5) != "this."){
38617                     format = "fm." + format + '(';
38618                 }else{
38619                     format = 'this.call("'+ format.substr(5) + '", ';
38620                     args = ", values";
38621                 }
38622                 
38623                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38624             }
38625              
38626             if (args.length) {
38627                 // called with xxyx.yuu:(test,test)
38628                 // change to ()
38629                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38630             }
38631             // raw.. - :raw modifier..
38632             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38633             
38634         };
38635         var body;
38636         // branched to use + in gecko and [].join() in others
38637         if(Roo.isGecko){
38638             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38639                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38640                     "';};};";
38641         }else{
38642             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38643             body.push(tpl.body.replace(/(\r\n|\n)/g,
38644                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38645             body.push("'].join('');};};");
38646             body = body.join('');
38647         }
38648         
38649         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38650        
38651         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38652         eval(body);
38653         
38654         return this;
38655     },
38656
38657     applyTemplate : function(values){
38658         return this.master.compiled.call(this, values, {});
38659         //var s = this.subs;
38660     },
38661
38662     apply : function(){
38663         return this.applyTemplate.apply(this, arguments);
38664     }
38665
38666  });
38667
38668 Roo.XTemplate.from = function(el){
38669     el = Roo.getDom(el);
38670     return new Roo.XTemplate(el.value || el.innerHTML);
38671 };