roojs-core.js
[roojs1] / roojs-ui-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11
12
13 /**
14  * @class Roo.data.SortTypes
15  * @singleton
16  * Defines the default sorting (casting?) comparison functions used when sorting data.
17  */
18 Roo.data.SortTypes = {
19     /**
20      * Default sort that does nothing
21      * @param {Mixed} s The value being converted
22      * @return {Mixed} The comparison value
23      */
24     none : function(s){
25         return s;
26     },
27     
28     /**
29      * The regular expression used to strip tags
30      * @type {RegExp}
31      * @property
32      */
33     stripTagsRE : /<\/?[^>]+>/gi,
34     
35     /**
36      * Strips all HTML tags to sort on text only
37      * @param {Mixed} s The value being converted
38      * @return {String} The comparison value
39      */
40     asText : function(s){
41         return String(s).replace(this.stripTagsRE, "");
42     },
43     
44     /**
45      * Strips all HTML tags to sort on text only - Case insensitive
46      * @param {Mixed} s The value being converted
47      * @return {String} The comparison value
48      */
49     asUCText : function(s){
50         return String(s).toUpperCase().replace(this.stripTagsRE, "");
51     },
52     
53     /**
54      * Case insensitive string
55      * @param {Mixed} s The value being converted
56      * @return {String} The comparison value
57      */
58     asUCString : function(s) {
59         return String(s).toUpperCase();
60     },
61     
62     /**
63      * Date sorting
64      * @param {Mixed} s The value being converted
65      * @return {Number} The comparison value
66      */
67     asDate : function(s) {
68         if(!s){
69             return 0;
70         }
71         if(s instanceof Date){
72             return s.getTime();
73         }
74         return Date.parse(String(s));
75     },
76     
77     /**
78      * Float sorting
79      * @param {Mixed} s The value being converted
80      * @return {Float} The comparison value
81      */
82     asFloat : function(s) {
83         var val = parseFloat(String(s).replace(/,/g, ""));
84         if(isNaN(val)) {
85             val = 0;
86         }
87         return val;
88     },
89     
90     /**
91      * Integer sorting
92      * @param {Mixed} s The value being converted
93      * @return {Number} The comparison value
94      */
95     asInt : function(s) {
96         var val = parseInt(String(s).replace(/,/g, ""));
97         if(isNaN(val)) {
98             val = 0;
99         }
100         return val;
101     }
102 };/*
103  * Based on:
104  * Ext JS Library 1.1.1
105  * Copyright(c) 2006-2007, Ext JS, LLC.
106  *
107  * Originally Released Under LGPL - original licence link has changed is not relivant.
108  *
109  * Fork - LGPL
110  * <script type="text/javascript">
111  */
112
113 /**
114 * @class Roo.data.Record
115  * Instances of this class encapsulate both record <em>definition</em> information, and record
116  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
117  * to access Records cached in an {@link Roo.data.Store} object.<br>
118  * <p>
119  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
120  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
121  * objects.<br>
122  * <p>
123  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
124  * @constructor
125  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
126  * {@link #create}. The parameters are the same.
127  * @param {Array} data An associative Array of data values keyed by the field name.
128  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
129  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
130  * not specified an integer id is generated.
131  */
132 Roo.data.Record = function(data, id){
133     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
134     this.data = data;
135 };
136
137 /**
138  * Generate a constructor for a specific record layout.
139  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
140  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
141  * Each field definition object may contain the following properties: <ul>
142  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
143  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
144  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
145  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
146  * is being used, then this is a string containing the javascript expression to reference the data relative to 
147  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
148  * to the data item relative to the record element. If the mapping expression is the same as the field name,
149  * this may be omitted.</p></li>
150  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
151  * <ul><li>auto (Default, implies no conversion)</li>
152  * <li>string</li>
153  * <li>int</li>
154  * <li>float</li>
155  * <li>boolean</li>
156  * <li>date</li></ul></p></li>
157  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
158  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
159  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
160  * by the Reader into an object that will be stored in the Record. It is passed the
161  * following parameters:<ul>
162  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
163  * </ul></p></li>
164  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
165  * </ul>
166  * <br>usage:<br><pre><code>
167 var TopicRecord = Roo.data.Record.create(
168     {name: 'title', mapping: 'topic_title'},
169     {name: 'author', mapping: 'username'},
170     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
171     {name: 'lastPost', mapping: 'post_time', type: 'date'},
172     {name: 'lastPoster', mapping: 'user2'},
173     {name: 'excerpt', mapping: 'post_text'}
174 );
175
176 var myNewRecord = new TopicRecord({
177     title: 'Do my job please',
178     author: 'noobie',
179     totalPosts: 1,
180     lastPost: new Date(),
181     lastPoster: 'Animal',
182     excerpt: 'No way dude!'
183 });
184 myStore.add(myNewRecord);
185 </code></pre>
186  * @method create
187  * @static
188  */
189 Roo.data.Record.create = function(o){
190     var f = function(){
191         f.superclass.constructor.apply(this, arguments);
192     };
193     Roo.extend(f, Roo.data.Record);
194     var p = f.prototype;
195     p.fields = new Roo.util.MixedCollection(false, function(field){
196         return field.name;
197     });
198     for(var i = 0, len = o.length; i < len; i++){
199         p.fields.add(new Roo.data.Field(o[i]));
200     }
201     f.getField = function(name){
202         return p.fields.get(name);  
203     };
204     return f;
205 };
206
207 Roo.data.Record.AUTO_ID = 1000;
208 Roo.data.Record.EDIT = 'edit';
209 Roo.data.Record.REJECT = 'reject';
210 Roo.data.Record.COMMIT = 'commit';
211
212 Roo.data.Record.prototype = {
213     /**
214      * Readonly flag - true if this record has been modified.
215      * @type Boolean
216      */
217     dirty : false,
218     editing : false,
219     error: null,
220     modified: null,
221
222     // private
223     join : function(store){
224         this.store = store;
225     },
226
227     /**
228      * Set the named field to the specified value.
229      * @param {String} name The name of the field to set.
230      * @param {Object} value The value to set the field to.
231      */
232     set : function(name, value){
233         if(this.data[name] == value){
234             return;
235         }
236         this.dirty = true;
237         if(!this.modified){
238             this.modified = {};
239         }
240         if(typeof this.modified[name] == 'undefined'){
241             this.modified[name] = this.data[name];
242         }
243         this.data[name] = value;
244         if(!this.editing && this.store){
245             this.store.afterEdit(this);
246         }       
247     },
248
249     /**
250      * Get the value of the named field.
251      * @param {String} name The name of the field to get the value of.
252      * @return {Object} The value of the field.
253      */
254     get : function(name){
255         return this.data[name]; 
256     },
257
258     // private
259     beginEdit : function(){
260         this.editing = true;
261         this.modified = {}; 
262     },
263
264     // private
265     cancelEdit : function(){
266         this.editing = false;
267         delete this.modified;
268     },
269
270     // private
271     endEdit : function(){
272         this.editing = false;
273         if(this.dirty && this.store){
274             this.store.afterEdit(this);
275         }
276     },
277
278     /**
279      * Usually called by the {@link Roo.data.Store} which owns the Record.
280      * Rejects all changes made to the Record since either creation, or the last commit operation.
281      * Modified fields are reverted to their original values.
282      * <p>
283      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
284      * of reject operations.
285      */
286     reject : function(){
287         var m = this.modified;
288         for(var n in m){
289             if(typeof m[n] != "function"){
290                 this.data[n] = m[n];
291             }
292         }
293         this.dirty = false;
294         delete this.modified;
295         this.editing = false;
296         if(this.store){
297             this.store.afterReject(this);
298         }
299     },
300
301     /**
302      * Usually called by the {@link Roo.data.Store} which owns the Record.
303      * Commits all changes made to the Record since either creation, or the last commit operation.
304      * <p>
305      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
306      * of commit operations.
307      */
308     commit : function(){
309         this.dirty = false;
310         delete this.modified;
311         this.editing = false;
312         if(this.store){
313             this.store.afterCommit(this);
314         }
315     },
316
317     // private
318     hasError : function(){
319         return this.error != null;
320     },
321
322     // private
323     clearError : function(){
324         this.error = null;
325     },
326
327     /**
328      * Creates a copy of this record.
329      * @param {String} id (optional) A new record id if you don't want to use this record's id
330      * @return {Record}
331      */
332     copy : function(newId) {
333         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
334     }
335 };/*
336  * Based on:
337  * Ext JS Library 1.1.1
338  * Copyright(c) 2006-2007, Ext JS, LLC.
339  *
340  * Originally Released Under LGPL - original licence link has changed is not relivant.
341  *
342  * Fork - LGPL
343  * <script type="text/javascript">
344  */
345
346
347
348 /**
349  * @class Roo.data.Store
350  * @extends Roo.util.Observable
351  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
352  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
353  * <p>
354  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
355  * has no knowledge of the format of the data returned by the Proxy.<br>
356  * <p>
357  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
358  * instances from the data object. These records are cached and made available through accessor functions.
359  * @constructor
360  * Creates a new Store.
361  * @param {Object} config A config object containing the objects needed for the Store to access data,
362  * and read the data into Records.
363  */
364 Roo.data.Store = function(config){
365     this.data = new Roo.util.MixedCollection(false);
366     this.data.getKey = function(o){
367         return o.id;
368     };
369     this.baseParams = {};
370     // private
371     this.paramNames = {
372         "start" : "start",
373         "limit" : "limit",
374         "sort" : "sort",
375         "dir" : "dir",
376         "multisort" : "_multisort"
377     };
378
379     if(config && config.data){
380         this.inlineData = config.data;
381         delete config.data;
382     }
383
384     Roo.apply(this, config);
385     
386     if(this.reader){ // reader passed
387         this.reader = Roo.factory(this.reader, Roo.data);
388         this.reader.xmodule = this.xmodule || false;
389         if(!this.recordType){
390             this.recordType = this.reader.recordType;
391         }
392         if(this.reader.onMetaChange){
393             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
394         }
395     }
396
397     if(this.recordType){
398         this.fields = this.recordType.prototype.fields;
399     }
400     this.modified = [];
401
402     this.addEvents({
403         /**
404          * @event datachanged
405          * Fires when the data cache has changed, and a widget which is using this Store
406          * as a Record cache should refresh its view.
407          * @param {Store} this
408          */
409         datachanged : true,
410         /**
411          * @event metachange
412          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
413          * @param {Store} this
414          * @param {Object} meta The JSON metadata
415          */
416         metachange : true,
417         /**
418          * @event add
419          * Fires when Records have been added to the Store
420          * @param {Store} this
421          * @param {Roo.data.Record[]} records The array of Records added
422          * @param {Number} index The index at which the record(s) were added
423          */
424         add : true,
425         /**
426          * @event remove
427          * Fires when a Record has been removed from the Store
428          * @param {Store} this
429          * @param {Roo.data.Record} record The Record that was removed
430          * @param {Number} index The index at which the record was removed
431          */
432         remove : true,
433         /**
434          * @event update
435          * Fires when a Record has been updated
436          * @param {Store} this
437          * @param {Roo.data.Record} record The Record that was updated
438          * @param {String} operation The update operation being performed.  Value may be one of:
439          * <pre><code>
440  Roo.data.Record.EDIT
441  Roo.data.Record.REJECT
442  Roo.data.Record.COMMIT
443          * </code></pre>
444          */
445         update : true,
446         /**
447          * @event clear
448          * Fires when the data cache has been cleared.
449          * @param {Store} this
450          */
451         clear : true,
452         /**
453          * @event beforeload
454          * Fires before a request is made for a new data object.  If the beforeload handler returns false
455          * the load action will be canceled.
456          * @param {Store} this
457          * @param {Object} options The loading options that were specified (see {@link #load} for details)
458          */
459         beforeload : true,
460         /**
461          * @event beforeloadadd
462          * Fires after a new set of Records has been loaded.
463          * @param {Store} this
464          * @param {Roo.data.Record[]} records The Records that were loaded
465          * @param {Object} options The loading options that were specified (see {@link #load} for details)
466          */
467         beforeloadadd : true,
468         /**
469          * @event load
470          * Fires after a new set of Records has been loaded, before they are added to the store.
471          * @param {Store} this
472          * @param {Roo.data.Record[]} records The Records that were loaded
473          * @param {Object} options The loading options that were specified (see {@link #load} for details)
474          * @params {Object} return from reader
475          */
476         load : true,
477         /**
478          * @event loadexception
479          * Fires if an exception occurs in the Proxy during loading.
480          * Called with the signature of the Proxy's "loadexception" event.
481          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
482          * 
483          * @param {Proxy} 
484          * @param {Object} return from JsonData.reader() - success, totalRecords, records
485          * @param {Object} load options 
486          * @param {Object} jsonData from your request (normally this contains the Exception)
487          */
488         loadexception : true
489     });
490     
491     if(this.proxy){
492         this.proxy = Roo.factory(this.proxy, Roo.data);
493         this.proxy.xmodule = this.xmodule || false;
494         this.relayEvents(this.proxy,  ["loadexception"]);
495     }
496     this.sortToggle = {};
497     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
498
499     Roo.data.Store.superclass.constructor.call(this);
500
501     if(this.inlineData){
502         this.loadData(this.inlineData);
503         delete this.inlineData;
504     }
505 };
506
507 Roo.extend(Roo.data.Store, Roo.util.Observable, {
508      /**
509     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
510     * without a remote query - used by combo/forms at present.
511     */
512     
513     /**
514     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
515     */
516     /**
517     * @cfg {Array} data Inline data to be loaded when the store is initialized.
518     */
519     /**
520     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
521     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
522     */
523     /**
524     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
525     * on any HTTP request
526     */
527     /**
528     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
529     */
530     /**
531     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
532     */
533     multiSort: false,
534     /**
535     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
536     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
537     */
538     remoteSort : false,
539
540     /**
541     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
542      * loaded or when a record is removed. (defaults to false).
543     */
544     pruneModifiedRecords : false,
545
546     // private
547     lastOptions : null,
548
549     /**
550      * Add Records to the Store and fires the add event.
551      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
552      */
553     add : function(records){
554         records = [].concat(records);
555         for(var i = 0, len = records.length; i < len; i++){
556             records[i].join(this);
557         }
558         var index = this.data.length;
559         this.data.addAll(records);
560         this.fireEvent("add", this, records, index);
561     },
562
563     /**
564      * Remove a Record from the Store and fires the remove event.
565      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
566      */
567     remove : function(record){
568         var index = this.data.indexOf(record);
569         this.data.removeAt(index);
570  
571         if(this.pruneModifiedRecords){
572             this.modified.remove(record);
573         }
574         this.fireEvent("remove", this, record, index);
575     },
576
577     /**
578      * Remove all Records from the Store and fires the clear event.
579      */
580     removeAll : function(){
581         this.data.clear();
582         if(this.pruneModifiedRecords){
583             this.modified = [];
584         }
585         this.fireEvent("clear", this);
586     },
587
588     /**
589      * Inserts Records to the Store at the given index and fires the add event.
590      * @param {Number} index The start index at which to insert the passed Records.
591      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
592      */
593     insert : function(index, records){
594         records = [].concat(records);
595         for(var i = 0, len = records.length; i < len; i++){
596             this.data.insert(index, records[i]);
597             records[i].join(this);
598         }
599         this.fireEvent("add", this, records, index);
600     },
601
602     /**
603      * Get the index within the cache of the passed Record.
604      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
605      * @return {Number} The index of the passed Record. Returns -1 if not found.
606      */
607     indexOf : function(record){
608         return this.data.indexOf(record);
609     },
610
611     /**
612      * Get the index within the cache of the Record with the passed id.
613      * @param {String} id The id of the Record to find.
614      * @return {Number} The index of the Record. Returns -1 if not found.
615      */
616     indexOfId : function(id){
617         return this.data.indexOfKey(id);
618     },
619
620     /**
621      * Get the Record with the specified id.
622      * @param {String} id The id of the Record to find.
623      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
624      */
625     getById : function(id){
626         return this.data.key(id);
627     },
628
629     /**
630      * Get the Record at the specified index.
631      * @param {Number} index The index of the Record to find.
632      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
633      */
634     getAt : function(index){
635         return this.data.itemAt(index);
636     },
637
638     /**
639      * Returns a range of Records between specified indices.
640      * @param {Number} startIndex (optional) The starting index (defaults to 0)
641      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
642      * @return {Roo.data.Record[]} An array of Records
643      */
644     getRange : function(start, end){
645         return this.data.getRange(start, end);
646     },
647
648     // private
649     storeOptions : function(o){
650         o = Roo.apply({}, o);
651         delete o.callback;
652         delete o.scope;
653         this.lastOptions = o;
654     },
655
656     /**
657      * Loads the Record cache from the configured Proxy using the configured Reader.
658      * <p>
659      * If using remote paging, then the first load call must specify the <em>start</em>
660      * and <em>limit</em> properties in the options.params property to establish the initial
661      * position within the dataset, and the number of Records to cache on each read from the Proxy.
662      * <p>
663      * <strong>It is important to note that for remote data sources, loading is asynchronous,
664      * and this call will return before the new data has been loaded. Perform any post-processing
665      * in a callback function, or in a "load" event handler.</strong>
666      * <p>
667      * @param {Object} options An object containing properties which control loading options:<ul>
668      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
669      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
670      * passed the following arguments:<ul>
671      * <li>r : Roo.data.Record[]</li>
672      * <li>options: Options object from the load call</li>
673      * <li>success: Boolean success indicator</li></ul></li>
674      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
675      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
676      * </ul>
677      */
678     load : function(options){
679         options = options || {};
680         if(this.fireEvent("beforeload", this, options) !== false){
681             this.storeOptions(options);
682             var p = Roo.apply(options.params || {}, this.baseParams);
683             // if meta was not loaded from remote source.. try requesting it.
684             if (!this.reader.metaFromRemote) {
685                 p._requestMeta = 1;
686             }
687             if(this.sortInfo && this.remoteSort){
688                 var pn = this.paramNames;
689                 p[pn["sort"]] = this.sortInfo.field;
690                 p[pn["dir"]] = this.sortInfo.direction;
691             }
692             if (this.multiSort) {
693                 var pn = this.paramNames;
694                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
695             }
696             
697             this.proxy.load(p, this.reader, this.loadRecords, this, options);
698         }
699     },
700
701     /**
702      * Reloads the Record cache from the configured Proxy using the configured Reader and
703      * the options from the last load operation performed.
704      * @param {Object} options (optional) An object containing properties which may override the options
705      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
706      * the most recently used options are reused).
707      */
708     reload : function(options){
709         this.load(Roo.applyIf(options||{}, this.lastOptions));
710     },
711
712     // private
713     // Called as a callback by the Reader during a load operation.
714     loadRecords : function(o, options, success){
715         if(!o || success === false){
716             if(success !== false){
717                 this.fireEvent("load", this, [], options, o);
718             }
719             if(options.callback){
720                 options.callback.call(options.scope || this, [], options, false);
721             }
722             return;
723         }
724         // if data returned failure - throw an exception.
725         if (o.success === false) {
726             // show a message if no listener is registered.
727             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
728                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
729             }
730             // loadmask wil be hooked into this..
731             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
732             return;
733         }
734         var r = o.records, t = o.totalRecords || r.length;
735         
736         this.fireEvent("beforeloadadd", this, r, options, o);
737         
738         if(!options || options.add !== true){
739             if(this.pruneModifiedRecords){
740                 this.modified = [];
741             }
742             for(var i = 0, len = r.length; i < len; i++){
743                 r[i].join(this);
744             }
745             if(this.snapshot){
746                 this.data = this.snapshot;
747                 delete this.snapshot;
748             }
749             this.data.clear();
750             this.data.addAll(r);
751             this.totalLength = t;
752             this.applySort();
753             this.fireEvent("datachanged", this);
754         }else{
755             this.totalLength = Math.max(t, this.data.length+r.length);
756             this.add(r);
757         }
758         
759         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
760                 
761             var e = new Roo.data.Record({});
762
763             e.set(this.parent.displayField, this.parent.emptyTitle);
764             e.set(this.parent.valueField, '');
765
766             this.insert(0, e);
767         }
768             
769         this.fireEvent("load", this, r, options, o);
770         if(options.callback){
771             options.callback.call(options.scope || this, r, options, true);
772         }
773     },
774
775
776     /**
777      * Loads data from a passed data block. A Reader which understands the format of the data
778      * must have been configured in the constructor.
779      * @param {Object} data The data block from which to read the Records.  The format of the data expected
780      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
781      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
782      */
783     loadData : function(o, append){
784         var r = this.reader.readRecords(o);
785         this.loadRecords(r, {add: append}, true);
786     },
787     
788      /**
789      * using 'cn' the nested child reader read the child array into it's child stores.
790      * @param {Object} rec The record with a 'children array
791      */
792     loadDataFromChildren : function(rec)
793     {
794         this.loadData(this.reader.toLoadData(rec));
795     },
796     
797
798     /**
799      * Gets the number of cached records.
800      * <p>
801      * <em>If using paging, this may not be the total size of the dataset. If the data object
802      * used by the Reader contains the dataset size, then the getTotalCount() function returns
803      * the data set size</em>
804      */
805     getCount : function(){
806         return this.data.length || 0;
807     },
808
809     /**
810      * Gets the total number of records in the dataset as returned by the server.
811      * <p>
812      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
813      * the dataset size</em>
814      */
815     getTotalCount : function(){
816         return this.totalLength || 0;
817     },
818
819     /**
820      * Returns the sort state of the Store as an object with two properties:
821      * <pre><code>
822  field {String} The name of the field by which the Records are sorted
823  direction {String} The sort order, "ASC" or "DESC"
824      * </code></pre>
825      */
826     getSortState : function(){
827         return this.sortInfo;
828     },
829
830     // private
831     applySort : function(){
832         if(this.sortInfo && !this.remoteSort){
833             var s = this.sortInfo, f = s.field;
834             var st = this.fields.get(f).sortType;
835             var fn = function(r1, r2){
836                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
837                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
838             };
839             this.data.sort(s.direction, fn);
840             if(this.snapshot && this.snapshot != this.data){
841                 this.snapshot.sort(s.direction, fn);
842             }
843         }
844     },
845
846     /**
847      * Sets the default sort column and order to be used by the next load operation.
848      * @param {String} fieldName The name of the field to sort by.
849      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
850      */
851     setDefaultSort : function(field, dir){
852         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
853     },
854
855     /**
856      * Sort the Records.
857      * If remote sorting is used, the sort is performed on the server, and the cache is
858      * reloaded. If local sorting is used, the cache is sorted internally.
859      * @param {String} fieldName The name of the field to sort by.
860      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
861      */
862     sort : function(fieldName, dir){
863         var f = this.fields.get(fieldName);
864         if(!dir){
865             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
866             
867             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
868                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
869             }else{
870                 dir = f.sortDir;
871             }
872         }
873         this.sortToggle[f.name] = dir;
874         this.sortInfo = {field: f.name, direction: dir};
875         if(!this.remoteSort){
876             this.applySort();
877             this.fireEvent("datachanged", this);
878         }else{
879             this.load(this.lastOptions);
880         }
881     },
882
883     /**
884      * Calls the specified function for each of the Records in the cache.
885      * @param {Function} fn The function to call. The Record is passed as the first parameter.
886      * Returning <em>false</em> aborts and exits the iteration.
887      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
888      */
889     each : function(fn, scope){
890         this.data.each(fn, scope);
891     },
892
893     /**
894      * Gets all records modified since the last commit.  Modified records are persisted across load operations
895      * (e.g., during paging).
896      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
897      */
898     getModifiedRecords : function(){
899         return this.modified;
900     },
901
902     // private
903     createFilterFn : function(property, value, anyMatch){
904         if(!value.exec){ // not a regex
905             value = String(value);
906             if(value.length == 0){
907                 return false;
908             }
909             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
910         }
911         return function(r){
912             return value.test(r.data[property]);
913         };
914     },
915
916     /**
917      * Sums the value of <i>property</i> for each record between start and end and returns the result.
918      * @param {String} property A field on your records
919      * @param {Number} start The record index to start at (defaults to 0)
920      * @param {Number} end The last record index to include (defaults to length - 1)
921      * @return {Number} The sum
922      */
923     sum : function(property, start, end){
924         var rs = this.data.items, v = 0;
925         start = start || 0;
926         end = (end || end === 0) ? end : rs.length-1;
927
928         for(var i = start; i <= end; i++){
929             v += (rs[i].data[property] || 0);
930         }
931         return v;
932     },
933
934     /**
935      * Filter the records by a specified property.
936      * @param {String} field A field on your records
937      * @param {String/RegExp} value Either a string that the field
938      * should start with or a RegExp to test against the field
939      * @param {Boolean} anyMatch True to match any part not just the beginning
940      */
941     filter : function(property, value, anyMatch){
942         var fn = this.createFilterFn(property, value, anyMatch);
943         return fn ? this.filterBy(fn) : this.clearFilter();
944     },
945
946     /**
947      * Filter by a function. The specified function will be called with each
948      * record in this data source. If the function returns true the record is included,
949      * otherwise it is filtered.
950      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
951      * @param {Object} scope (optional) The scope of the function (defaults to this)
952      */
953     filterBy : function(fn, scope){
954         this.snapshot = this.snapshot || this.data;
955         this.data = this.queryBy(fn, scope||this);
956         this.fireEvent("datachanged", this);
957     },
958
959     /**
960      * Query the records by a specified property.
961      * @param {String} field A field on your records
962      * @param {String/RegExp} value Either a string that the field
963      * should start with or a RegExp to test against the field
964      * @param {Boolean} anyMatch True to match any part not just the beginning
965      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
966      */
967     query : function(property, value, anyMatch){
968         var fn = this.createFilterFn(property, value, anyMatch);
969         return fn ? this.queryBy(fn) : this.data.clone();
970     },
971
972     /**
973      * Query by a function. The specified function will be called with each
974      * record in this data source. If the function returns true the record is included
975      * in the results.
976      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
977      * @param {Object} scope (optional) The scope of the function (defaults to this)
978       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
979      **/
980     queryBy : function(fn, scope){
981         var data = this.snapshot || this.data;
982         return data.filterBy(fn, scope||this);
983     },
984
985     /**
986      * Collects unique values for a particular dataIndex from this store.
987      * @param {String} dataIndex The property to collect
988      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
989      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
990      * @return {Array} An array of the unique values
991      **/
992     collect : function(dataIndex, allowNull, bypassFilter){
993         var d = (bypassFilter === true && this.snapshot) ?
994                 this.snapshot.items : this.data.items;
995         var v, sv, r = [], l = {};
996         for(var i = 0, len = d.length; i < len; i++){
997             v = d[i].data[dataIndex];
998             sv = String(v);
999             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
1000                 l[sv] = true;
1001                 r[r.length] = v;
1002             }
1003         }
1004         return r;
1005     },
1006
1007     /**
1008      * Revert to a view of the Record cache with no filtering applied.
1009      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
1010      */
1011     clearFilter : function(suppressEvent){
1012         if(this.snapshot && this.snapshot != this.data){
1013             this.data = this.snapshot;
1014             delete this.snapshot;
1015             if(suppressEvent !== true){
1016                 this.fireEvent("datachanged", this);
1017             }
1018         }
1019     },
1020
1021     // private
1022     afterEdit : function(record){
1023         if(this.modified.indexOf(record) == -1){
1024             this.modified.push(record);
1025         }
1026         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
1027     },
1028     
1029     // private
1030     afterReject : function(record){
1031         this.modified.remove(record);
1032         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
1033     },
1034
1035     // private
1036     afterCommit : function(record){
1037         this.modified.remove(record);
1038         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
1039     },
1040
1041     /**
1042      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
1043      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
1044      */
1045     commitChanges : function(){
1046         var m = this.modified.slice(0);
1047         this.modified = [];
1048         for(var i = 0, len = m.length; i < len; i++){
1049             m[i].commit();
1050         }
1051     },
1052
1053     /**
1054      * Cancel outstanding changes on all changed records.
1055      */
1056     rejectChanges : function(){
1057         var m = this.modified.slice(0);
1058         this.modified = [];
1059         for(var i = 0, len = m.length; i < len; i++){
1060             m[i].reject();
1061         }
1062     },
1063
1064     onMetaChange : function(meta, rtype, o){
1065         this.recordType = rtype;
1066         this.fields = rtype.prototype.fields;
1067         delete this.snapshot;
1068         this.sortInfo = meta.sortInfo || this.sortInfo;
1069         this.modified = [];
1070         this.fireEvent('metachange', this, this.reader.meta);
1071     },
1072     
1073     moveIndex : function(data, type)
1074     {
1075         var index = this.indexOf(data);
1076         
1077         var newIndex = index + type;
1078         
1079         this.remove(data);
1080         
1081         this.insert(newIndex, data);
1082         
1083     }
1084 });/*
1085  * Based on:
1086  * Ext JS Library 1.1.1
1087  * Copyright(c) 2006-2007, Ext JS, LLC.
1088  *
1089  * Originally Released Under LGPL - original licence link has changed is not relivant.
1090  *
1091  * Fork - LGPL
1092  * <script type="text/javascript">
1093  */
1094
1095 /**
1096  * @class Roo.data.SimpleStore
1097  * @extends Roo.data.Store
1098  * Small helper class to make creating Stores from Array data easier.
1099  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
1100  * @cfg {Array} fields An array of field definition objects, or field name strings.
1101  * @cfg {Object} an existing reader (eg. copied from another store)
1102  * @cfg {Array} data The multi-dimensional array of data
1103  * @constructor
1104  * @param {Object} config
1105  */
1106 Roo.data.SimpleStore = function(config)
1107 {
1108     Roo.data.SimpleStore.superclass.constructor.call(this, {
1109         isLocal : true,
1110         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
1111                 id: config.id
1112             },
1113             Roo.data.Record.create(config.fields)
1114         ),
1115         proxy : new Roo.data.MemoryProxy(config.data)
1116     });
1117     this.load();
1118 };
1119 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
1120  * Based on:
1121  * Ext JS Library 1.1.1
1122  * Copyright(c) 2006-2007, Ext JS, LLC.
1123  *
1124  * Originally Released Under LGPL - original licence link has changed is not relivant.
1125  *
1126  * Fork - LGPL
1127  * <script type="text/javascript">
1128  */
1129
1130 /**
1131 /**
1132  * @extends Roo.data.Store
1133  * @class Roo.data.JsonStore
1134  * Small helper class to make creating Stores for JSON data easier. <br/>
1135 <pre><code>
1136 var store = new Roo.data.JsonStore({
1137     url: 'get-images.php',
1138     root: 'images',
1139     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
1140 });
1141 </code></pre>
1142  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
1143  * JsonReader and HttpProxy (unless inline data is provided).</b>
1144  * @cfg {Array} fields An array of field definition objects, or field name strings.
1145  * @constructor
1146  * @param {Object} config
1147  */
1148 Roo.data.JsonStore = function(c){
1149     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
1150         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
1151         reader: new Roo.data.JsonReader(c, c.fields)
1152     }));
1153 };
1154 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
1155  * Based on:
1156  * Ext JS Library 1.1.1
1157  * Copyright(c) 2006-2007, Ext JS, LLC.
1158  *
1159  * Originally Released Under LGPL - original licence link has changed is not relivant.
1160  *
1161  * Fork - LGPL
1162  * <script type="text/javascript">
1163  */
1164
1165  
1166 Roo.data.Field = function(config){
1167     if(typeof config == "string"){
1168         config = {name: config};
1169     }
1170     Roo.apply(this, config);
1171     
1172     if(!this.type){
1173         this.type = "auto";
1174     }
1175     
1176     var st = Roo.data.SortTypes;
1177     // named sortTypes are supported, here we look them up
1178     if(typeof this.sortType == "string"){
1179         this.sortType = st[this.sortType];
1180     }
1181     
1182     // set default sortType for strings and dates
1183     if(!this.sortType){
1184         switch(this.type){
1185             case "string":
1186                 this.sortType = st.asUCString;
1187                 break;
1188             case "date":
1189                 this.sortType = st.asDate;
1190                 break;
1191             default:
1192                 this.sortType = st.none;
1193         }
1194     }
1195
1196     // define once
1197     var stripRe = /[\$,%]/g;
1198
1199     // prebuilt conversion function for this field, instead of
1200     // switching every time we're reading a value
1201     if(!this.convert){
1202         var cv, dateFormat = this.dateFormat;
1203         switch(this.type){
1204             case "":
1205             case "auto":
1206             case undefined:
1207                 cv = function(v){ return v; };
1208                 break;
1209             case "string":
1210                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
1211                 break;
1212             case "int":
1213                 cv = function(v){
1214                     return v !== undefined && v !== null && v !== '' ?
1215                            parseInt(String(v).replace(stripRe, ""), 10) : '';
1216                     };
1217                 break;
1218             case "float":
1219                 cv = function(v){
1220                     return v !== undefined && v !== null && v !== '' ?
1221                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
1222                     };
1223                 break;
1224             case "bool":
1225             case "boolean":
1226                 cv = function(v){ return v === true || v === "true" || v == 1; };
1227                 break;
1228             case "date":
1229                 cv = function(v){
1230                     if(!v){
1231                         return '';
1232                     }
1233                     if(v instanceof Date){
1234                         return v;
1235                     }
1236                     if(dateFormat){
1237                         if(dateFormat == "timestamp"){
1238                             return new Date(v*1000);
1239                         }
1240                         return Date.parseDate(v, dateFormat);
1241                     }
1242                     var parsed = Date.parse(v);
1243                     return parsed ? new Date(parsed) : null;
1244                 };
1245              break;
1246             
1247         }
1248         this.convert = cv;
1249     }
1250 };
1251
1252 Roo.data.Field.prototype = {
1253     dateFormat: null,
1254     defaultValue: "",
1255     mapping: null,
1256     sortType : null,
1257     sortDir : "ASC"
1258 };/*
1259  * Based on:
1260  * Ext JS Library 1.1.1
1261  * Copyright(c) 2006-2007, Ext JS, LLC.
1262  *
1263  * Originally Released Under LGPL - original licence link has changed is not relivant.
1264  *
1265  * Fork - LGPL
1266  * <script type="text/javascript">
1267  */
1268  
1269 // Base class for reading structured data from a data source.  This class is intended to be
1270 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
1271
1272 /**
1273  * @class Roo.data.DataReader
1274  * Base class for reading structured data from a data source.  This class is intended to be
1275  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
1276  */
1277
1278 Roo.data.DataReader = function(meta, recordType){
1279     
1280     this.meta = meta;
1281     
1282     this.recordType = recordType instanceof Array ? 
1283         Roo.data.Record.create(recordType) : recordType;
1284 };
1285
1286 Roo.data.DataReader.prototype = {
1287     
1288     
1289     readerType : 'Data',
1290      /**
1291      * Create an empty record
1292      * @param {Object} data (optional) - overlay some values
1293      * @return {Roo.data.Record} record created.
1294      */
1295     newRow :  function(d) {
1296         var da =  {};
1297         this.recordType.prototype.fields.each(function(c) {
1298             switch( c.type) {
1299                 case 'int' : da[c.name] = 0; break;
1300                 case 'date' : da[c.name] = new Date(); break;
1301                 case 'float' : da[c.name] = 0.0; break;
1302                 case 'boolean' : da[c.name] = false; break;
1303                 default : da[c.name] = ""; break;
1304             }
1305             
1306         });
1307         return new this.recordType(Roo.apply(da, d));
1308     }
1309     
1310     
1311 };/*
1312  * Based on:
1313  * Ext JS Library 1.1.1
1314  * Copyright(c) 2006-2007, Ext JS, LLC.
1315  *
1316  * Originally Released Under LGPL - original licence link has changed is not relivant.
1317  *
1318  * Fork - LGPL
1319  * <script type="text/javascript">
1320  */
1321
1322 /**
1323  * @class Roo.data.DataProxy
1324  * @extends Roo.data.Observable
1325  * This class is an abstract base class for implementations which provide retrieval of
1326  * unformatted data objects.<br>
1327  * <p>
1328  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
1329  * (of the appropriate type which knows how to parse the data object) to provide a block of
1330  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
1331  * <p>
1332  * Custom implementations must implement the load method as described in
1333  * {@link Roo.data.HttpProxy#load}.
1334  */
1335 Roo.data.DataProxy = function(){
1336     this.addEvents({
1337         /**
1338          * @event beforeload
1339          * Fires before a network request is made to retrieve a data object.
1340          * @param {Object} This DataProxy object.
1341          * @param {Object} params The params parameter to the load function.
1342          */
1343         beforeload : true,
1344         /**
1345          * @event load
1346          * Fires before the load method's callback is called.
1347          * @param {Object} This DataProxy object.
1348          * @param {Object} o The data object.
1349          * @param {Object} arg The callback argument object passed to the load function.
1350          */
1351         load : true,
1352         /**
1353          * @event loadexception
1354          * Fires if an Exception occurs during data retrieval.
1355          * @param {Object} This DataProxy object.
1356          * @param {Object} o The data object.
1357          * @param {Object} arg The callback argument object passed to the load function.
1358          * @param {Object} e The Exception.
1359          */
1360         loadexception : true
1361     });
1362     Roo.data.DataProxy.superclass.constructor.call(this);
1363 };
1364
1365 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
1366
1367     /**
1368      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
1369      */
1370 /*
1371  * Based on:
1372  * Ext JS Library 1.1.1
1373  * Copyright(c) 2006-2007, Ext JS, LLC.
1374  *
1375  * Originally Released Under LGPL - original licence link has changed is not relivant.
1376  *
1377  * Fork - LGPL
1378  * <script type="text/javascript">
1379  */
1380 /**
1381  * @class Roo.data.MemoryProxy
1382  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
1383  * to the Reader when its load method is called.
1384  * @constructor
1385  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
1386  */
1387 Roo.data.MemoryProxy = function(data){
1388     if (data.data) {
1389         data = data.data;
1390     }
1391     Roo.data.MemoryProxy.superclass.constructor.call(this);
1392     this.data = data;
1393 };
1394
1395 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
1396     
1397     /**
1398      * Load data from the requested source (in this case an in-memory
1399      * data object passed to the constructor), read the data object into
1400      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1401      * process that block using the passed callback.
1402      * @param {Object} params This parameter is not used by the MemoryProxy class.
1403      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1404      * object into a block of Roo.data.Records.
1405      * @param {Function} callback The function into which to pass the block of Roo.data.records.
1406      * The function must be passed <ul>
1407      * <li>The Record block object</li>
1408      * <li>The "arg" argument from the load function</li>
1409      * <li>A boolean success indicator</li>
1410      * </ul>
1411      * @param {Object} scope The scope in which to call the callback
1412      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1413      */
1414     load : function(params, reader, callback, scope, arg){
1415         params = params || {};
1416         var result;
1417         try {
1418             result = reader.readRecords(params.data ? params.data :this.data);
1419         }catch(e){
1420             this.fireEvent("loadexception", this, arg, null, e);
1421             callback.call(scope, null, arg, false);
1422             return;
1423         }
1424         callback.call(scope, result, arg, true);
1425     },
1426     
1427     // private
1428     update : function(params, records){
1429         
1430     }
1431 });/*
1432  * Based on:
1433  * Ext JS Library 1.1.1
1434  * Copyright(c) 2006-2007, Ext JS, LLC.
1435  *
1436  * Originally Released Under LGPL - original licence link has changed is not relivant.
1437  *
1438  * Fork - LGPL
1439  * <script type="text/javascript">
1440  */
1441 /**
1442  * @class Roo.data.HttpProxy
1443  * @extends Roo.data.DataProxy
1444  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
1445  * configured to reference a certain URL.<br><br>
1446  * <p>
1447  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
1448  * from which the running page was served.<br><br>
1449  * <p>
1450  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
1451  * <p>
1452  * Be aware that to enable the browser to parse an XML document, the server must set
1453  * the Content-Type header in the HTTP response to "text/xml".
1454  * @constructor
1455  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
1456  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
1457  * will be used to make the request.
1458  */
1459 Roo.data.HttpProxy = function(conn){
1460     Roo.data.HttpProxy.superclass.constructor.call(this);
1461     // is conn a conn config or a real conn?
1462     this.conn = conn;
1463     this.useAjax = !conn || !conn.events;
1464   
1465 };
1466
1467 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
1468     // thse are take from connection...
1469     
1470     /**
1471      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
1472      */
1473     /**
1474      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
1475      * extra parameters to each request made by this object. (defaults to undefined)
1476      */
1477     /**
1478      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
1479      *  to each request made by this object. (defaults to undefined)
1480      */
1481     /**
1482      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
1483      */
1484     /**
1485      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
1486      */
1487      /**
1488      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
1489      * @type Boolean
1490      */
1491   
1492
1493     /**
1494      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
1495      * @type Boolean
1496      */
1497     /**
1498      * Return the {@link Roo.data.Connection} object being used by this Proxy.
1499      * @return {Connection} The Connection object. This object may be used to subscribe to events on
1500      * a finer-grained basis than the DataProxy events.
1501      */
1502     getConnection : function(){
1503         return this.useAjax ? Roo.Ajax : this.conn;
1504     },
1505
1506     /**
1507      * Load data from the configured {@link Roo.data.Connection}, read the data object into
1508      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
1509      * process that block using the passed callback.
1510      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1511      * for the request to the remote server.
1512      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1513      * object into a block of Roo.data.Records.
1514      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1515      * The function must be passed <ul>
1516      * <li>The Record block object</li>
1517      * <li>The "arg" argument from the load function</li>
1518      * <li>A boolean success indicator</li>
1519      * </ul>
1520      * @param {Object} scope The scope in which to call the callback
1521      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1522      */
1523     load : function(params, reader, callback, scope, arg){
1524         if(this.fireEvent("beforeload", this, params) !== false){
1525             var  o = {
1526                 params : params || {},
1527                 request: {
1528                     callback : callback,
1529                     scope : scope,
1530                     arg : arg
1531                 },
1532                 reader: reader,
1533                 callback : this.loadResponse,
1534                 scope: this
1535             };
1536             if(this.useAjax){
1537                 Roo.applyIf(o, this.conn);
1538                 if(this.activeRequest){
1539                     Roo.Ajax.abort(this.activeRequest);
1540                 }
1541                 this.activeRequest = Roo.Ajax.request(o);
1542             }else{
1543                 this.conn.request(o);
1544             }
1545         }else{
1546             callback.call(scope||this, null, arg, false);
1547         }
1548     },
1549
1550     // private
1551     loadResponse : function(o, success, response){
1552         delete this.activeRequest;
1553         if(!success){
1554             this.fireEvent("loadexception", this, o, response);
1555             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1556             return;
1557         }
1558         var result;
1559         try {
1560             result = o.reader.read(response);
1561         }catch(e){
1562             this.fireEvent("loadexception", this, o, response, e);
1563             o.request.callback.call(o.request.scope, null, o.request.arg, false);
1564             return;
1565         }
1566         
1567         this.fireEvent("load", this, o, o.request.arg);
1568         o.request.callback.call(o.request.scope, result, o.request.arg, true);
1569     },
1570
1571     // private
1572     update : function(dataSet){
1573
1574     },
1575
1576     // private
1577     updateResponse : function(dataSet){
1578
1579     }
1580 });/*
1581  * Based on:
1582  * Ext JS Library 1.1.1
1583  * Copyright(c) 2006-2007, Ext JS, LLC.
1584  *
1585  * Originally Released Under LGPL - original licence link has changed is not relivant.
1586  *
1587  * Fork - LGPL
1588  * <script type="text/javascript">
1589  */
1590
1591 /**
1592  * @class Roo.data.ScriptTagProxy
1593  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
1594  * other than the originating domain of the running page.<br><br>
1595  * <p>
1596  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
1597  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
1598  * <p>
1599  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
1600  * source code that is used as the source inside a &lt;script> tag.<br><br>
1601  * <p>
1602  * In order for the browser to process the returned data, the server must wrap the data object
1603  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
1604  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
1605  * depending on whether the callback name was passed:
1606  * <p>
1607  * <pre><code>
1608 boolean scriptTag = false;
1609 String cb = request.getParameter("callback");
1610 if (cb != null) {
1611     scriptTag = true;
1612     response.setContentType("text/javascript");
1613 } else {
1614     response.setContentType("application/x-json");
1615 }
1616 Writer out = response.getWriter();
1617 if (scriptTag) {
1618     out.write(cb + "(");
1619 }
1620 out.print(dataBlock.toJsonString());
1621 if (scriptTag) {
1622     out.write(");");
1623 }
1624 </pre></code>
1625  *
1626  * @constructor
1627  * @param {Object} config A configuration object.
1628  */
1629 Roo.data.ScriptTagProxy = function(config){
1630     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
1631     Roo.apply(this, config);
1632     this.head = document.getElementsByTagName("head")[0];
1633 };
1634
1635 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
1636
1637 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
1638     /**
1639      * @cfg {String} url The URL from which to request the data object.
1640      */
1641     /**
1642      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
1643      */
1644     timeout : 30000,
1645     /**
1646      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
1647      * the server the name of the callback function set up by the load call to process the returned data object.
1648      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
1649      * javascript output which calls this named function passing the data object as its only parameter.
1650      */
1651     callbackParam : "callback",
1652     /**
1653      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
1654      * name to the request.
1655      */
1656     nocache : true,
1657
1658     /**
1659      * Load data from the configured URL, read the data object into
1660      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
1661      * process that block using the passed callback.
1662      * @param {Object} params An object containing properties which are to be used as HTTP parameters
1663      * for the request to the remote server.
1664      * @param {Roo.data.DataReader} reader The Reader object which converts the data
1665      * object into a block of Roo.data.Records.
1666      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
1667      * The function must be passed <ul>
1668      * <li>The Record block object</li>
1669      * <li>The "arg" argument from the load function</li>
1670      * <li>A boolean success indicator</li>
1671      * </ul>
1672      * @param {Object} scope The scope in which to call the callback
1673      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
1674      */
1675     load : function(params, reader, callback, scope, arg){
1676         if(this.fireEvent("beforeload", this, params) !== false){
1677
1678             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
1679
1680             var url = this.url;
1681             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
1682             if(this.nocache){
1683                 url += "&_dc=" + (new Date().getTime());
1684             }
1685             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
1686             var trans = {
1687                 id : transId,
1688                 cb : "stcCallback"+transId,
1689                 scriptId : "stcScript"+transId,
1690                 params : params,
1691                 arg : arg,
1692                 url : url,
1693                 callback : callback,
1694                 scope : scope,
1695                 reader : reader
1696             };
1697             var conn = this;
1698
1699             window[trans.cb] = function(o){
1700                 conn.handleResponse(o, trans);
1701             };
1702
1703             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
1704
1705             if(this.autoAbort !== false){
1706                 this.abort();
1707             }
1708
1709             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
1710
1711             var script = document.createElement("script");
1712             script.setAttribute("src", url);
1713             script.setAttribute("type", "text/javascript");
1714             script.setAttribute("id", trans.scriptId);
1715             this.head.appendChild(script);
1716
1717             this.trans = trans;
1718         }else{
1719             callback.call(scope||this, null, arg, false);
1720         }
1721     },
1722
1723     // private
1724     isLoading : function(){
1725         return this.trans ? true : false;
1726     },
1727
1728     /**
1729      * Abort the current server request.
1730      */
1731     abort : function(){
1732         if(this.isLoading()){
1733             this.destroyTrans(this.trans);
1734         }
1735     },
1736
1737     // private
1738     destroyTrans : function(trans, isLoaded){
1739         this.head.removeChild(document.getElementById(trans.scriptId));
1740         clearTimeout(trans.timeoutId);
1741         if(isLoaded){
1742             window[trans.cb] = undefined;
1743             try{
1744                 delete window[trans.cb];
1745             }catch(e){}
1746         }else{
1747             // if hasn't been loaded, wait for load to remove it to prevent script error
1748             window[trans.cb] = function(){
1749                 window[trans.cb] = undefined;
1750                 try{
1751                     delete window[trans.cb];
1752                 }catch(e){}
1753             };
1754         }
1755     },
1756
1757     // private
1758     handleResponse : function(o, trans){
1759         this.trans = false;
1760         this.destroyTrans(trans, true);
1761         var result;
1762         try {
1763             result = trans.reader.readRecords(o);
1764         }catch(e){
1765             this.fireEvent("loadexception", this, o, trans.arg, e);
1766             trans.callback.call(trans.scope||window, null, trans.arg, false);
1767             return;
1768         }
1769         this.fireEvent("load", this, o, trans.arg);
1770         trans.callback.call(trans.scope||window, result, trans.arg, true);
1771     },
1772
1773     // private
1774     handleFailure : function(trans){
1775         this.trans = false;
1776         this.destroyTrans(trans, false);
1777         this.fireEvent("loadexception", this, null, trans.arg);
1778         trans.callback.call(trans.scope||window, null, trans.arg, false);
1779     }
1780 });/*
1781  * Based on:
1782  * Ext JS Library 1.1.1
1783  * Copyright(c) 2006-2007, Ext JS, LLC.
1784  *
1785  * Originally Released Under LGPL - original licence link has changed is not relivant.
1786  *
1787  * Fork - LGPL
1788  * <script type="text/javascript">
1789  */
1790
1791 /**
1792  * @class Roo.data.JsonReader
1793  * @extends Roo.data.DataReader
1794  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
1795  * based on mappings in a provided Roo.data.Record constructor.
1796  * 
1797  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
1798  * in the reply previously. 
1799  * 
1800  * <p>
1801  * Example code:
1802  * <pre><code>
1803 var RecordDef = Roo.data.Record.create([
1804     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
1805     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
1806 ]);
1807 var myReader = new Roo.data.JsonReader({
1808     totalProperty: "results",    // The property which contains the total dataset size (optional)
1809     root: "rows",                // The property which contains an Array of row objects
1810     id: "id"                     // The property within each row object that provides an ID for the record (optional)
1811 }, RecordDef);
1812 </code></pre>
1813  * <p>
1814  * This would consume a JSON file like this:
1815  * <pre><code>
1816 { 'results': 2, 'rows': [
1817     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
1818     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
1819 }
1820 </code></pre>
1821  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
1822  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
1823  * paged from the remote server.
1824  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
1825  * @cfg {String} root name of the property which contains the Array of row objects.
1826  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
1827  * @cfg {Array} fields Array of field definition objects
1828  * @constructor
1829  * Create a new JsonReader
1830  * @param {Object} meta Metadata configuration options
1831  * @param {Object} recordType Either an Array of field definition objects,
1832  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
1833  */
1834 Roo.data.JsonReader = function(meta, recordType){
1835     
1836     meta = meta || {};
1837     // set some defaults:
1838     Roo.applyIf(meta, {
1839         totalProperty: 'total',
1840         successProperty : 'success',
1841         root : 'data',
1842         id : 'id'
1843     });
1844     
1845     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
1846 };
1847 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
1848     
1849     readerType : 'Json',
1850     
1851     /**
1852      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
1853      * Used by Store query builder to append _requestMeta to params.
1854      * 
1855      */
1856     metaFromRemote : false,
1857     /**
1858      * This method is only used by a DataProxy which has retrieved data from a remote server.
1859      * @param {Object} response The XHR object which contains the JSON data in its responseText.
1860      * @return {Object} data A data block which is used by an Roo.data.Store object as
1861      * a cache of Roo.data.Records.
1862      */
1863     read : function(response){
1864         var json = response.responseText;
1865        
1866         var o = /* eval:var:o */ eval("("+json+")");
1867         if(!o) {
1868             throw {message: "JsonReader.read: Json object not found"};
1869         }
1870         
1871         if(o.metaData){
1872             
1873             delete this.ef;
1874             this.metaFromRemote = true;
1875             this.meta = o.metaData;
1876             this.recordType = Roo.data.Record.create(o.metaData.fields);
1877             this.onMetaChange(this.meta, this.recordType, o);
1878         }
1879         return this.readRecords(o);
1880     },
1881
1882     // private function a store will implement
1883     onMetaChange : function(meta, recordType, o){
1884
1885     },
1886
1887     /**
1888          * @ignore
1889          */
1890     simpleAccess: function(obj, subsc) {
1891         return obj[subsc];
1892     },
1893
1894         /**
1895          * @ignore
1896          */
1897     getJsonAccessor: function(){
1898         var re = /[\[\.]/;
1899         return function(expr) {
1900             try {
1901                 return(re.test(expr))
1902                     ? new Function("obj", "return obj." + expr)
1903                     : function(obj){
1904                         return obj[expr];
1905                     };
1906             } catch(e){}
1907             return Roo.emptyFn;
1908         };
1909     }(),
1910
1911     /**
1912      * Create a data block containing Roo.data.Records from an XML document.
1913      * @param {Object} o An object which contains an Array of row objects in the property specified
1914      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
1915      * which contains the total size of the dataset.
1916      * @return {Object} data A data block which is used by an Roo.data.Store object as
1917      * a cache of Roo.data.Records.
1918      */
1919     readRecords : function(o){
1920         /**
1921          * After any data loads, the raw JSON data is available for further custom processing.
1922          * @type Object
1923          */
1924         this.o = o;
1925         var s = this.meta, Record = this.recordType,
1926             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
1927
1928 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
1929         if (!this.ef) {
1930             if(s.totalProperty) {
1931                     this.getTotal = this.getJsonAccessor(s.totalProperty);
1932                 }
1933                 if(s.successProperty) {
1934                     this.getSuccess = this.getJsonAccessor(s.successProperty);
1935                 }
1936                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
1937                 if (s.id) {
1938                         var g = this.getJsonAccessor(s.id);
1939                         this.getId = function(rec) {
1940                                 var r = g(rec);  
1941                                 return (r === undefined || r === "") ? null : r;
1942                         };
1943                 } else {
1944                         this.getId = function(){return null;};
1945                 }
1946             this.ef = [];
1947             for(var jj = 0; jj < fl; jj++){
1948                 f = fi[jj];
1949                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
1950                 this.ef[jj] = this.getJsonAccessor(map);
1951             }
1952         }
1953
1954         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
1955         if(s.totalProperty){
1956             var vt = parseInt(this.getTotal(o), 10);
1957             if(!isNaN(vt)){
1958                 totalRecords = vt;
1959             }
1960         }
1961         if(s.successProperty){
1962             var vs = this.getSuccess(o);
1963             if(vs === false || vs === 'false'){
1964                 success = false;
1965             }
1966         }
1967         var records = [];
1968         for(var i = 0; i < c; i++){
1969                 var n = root[i];
1970             var values = {};
1971             var id = this.getId(n);
1972             for(var j = 0; j < fl; j++){
1973                 f = fi[j];
1974             var v = this.ef[j](n);
1975             if (!f.convert) {
1976                 Roo.log('missing convert for ' + f.name);
1977                 Roo.log(f);
1978                 continue;
1979             }
1980             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
1981             }
1982             var record = new Record(values, id);
1983             record.json = n;
1984             records[i] = record;
1985         }
1986         return {
1987             raw : o,
1988             success : success,
1989             records : records,
1990             totalRecords : totalRecords
1991         };
1992     },
1993     // used when loading children.. @see loadDataFromChildren
1994     toLoadData: function(rec)
1995     {
1996         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
1997         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
1998         return { data : data, total : data.length };
1999         
2000     }
2001 });/*
2002  * Based on:
2003  * Ext JS Library 1.1.1
2004  * Copyright(c) 2006-2007, Ext JS, LLC.
2005  *
2006  * Originally Released Under LGPL - original licence link has changed is not relivant.
2007  *
2008  * Fork - LGPL
2009  * <script type="text/javascript">
2010  */
2011
2012 /**
2013  * @class Roo.data.XmlReader
2014  * @extends Roo.data.DataReader
2015  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
2016  * based on mappings in a provided Roo.data.Record constructor.<br><br>
2017  * <p>
2018  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
2019  * header in the HTTP response must be set to "text/xml".</em>
2020  * <p>
2021  * Example code:
2022  * <pre><code>
2023 var RecordDef = Roo.data.Record.create([
2024    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
2025    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
2026 ]);
2027 var myReader = new Roo.data.XmlReader({
2028    totalRecords: "results", // The element which contains the total dataset size (optional)
2029    record: "row",           // The repeated element which contains row information
2030    id: "id"                 // The element within the row that provides an ID for the record (optional)
2031 }, RecordDef);
2032 </code></pre>
2033  * <p>
2034  * This would consume an XML file like this:
2035  * <pre><code>
2036 &lt;?xml?>
2037 &lt;dataset>
2038  &lt;results>2&lt;/results>
2039  &lt;row>
2040    &lt;id>1&lt;/id>
2041    &lt;name>Bill&lt;/name>
2042    &lt;occupation>Gardener&lt;/occupation>
2043  &lt;/row>
2044  &lt;row>
2045    &lt;id>2&lt;/id>
2046    &lt;name>Ben&lt;/name>
2047    &lt;occupation>Horticulturalist&lt;/occupation>
2048  &lt;/row>
2049 &lt;/dataset>
2050 </code></pre>
2051  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
2052  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
2053  * paged from the remote server.
2054  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
2055  * @cfg {String} success The DomQuery path to the success attribute used by forms.
2056  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
2057  * a record identifier value.
2058  * @constructor
2059  * Create a new XmlReader
2060  * @param {Object} meta Metadata configuration options
2061  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
2062  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
2063  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
2064  */
2065 Roo.data.XmlReader = function(meta, recordType){
2066     meta = meta || {};
2067     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2068 };
2069 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
2070     
2071     readerType : 'Xml',
2072     
2073     /**
2074      * This method is only used by a DataProxy which has retrieved data from a remote server.
2075          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
2076          * to contain a method called 'responseXML' that returns an XML document object.
2077      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2078      * a cache of Roo.data.Records.
2079      */
2080     read : function(response){
2081         var doc = response.responseXML;
2082         if(!doc) {
2083             throw {message: "XmlReader.read: XML Document not available"};
2084         }
2085         return this.readRecords(doc);
2086     },
2087
2088     /**
2089      * Create a data block containing Roo.data.Records from an XML document.
2090          * @param {Object} doc A parsed XML document.
2091      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
2092      * a cache of Roo.data.Records.
2093      */
2094     readRecords : function(doc){
2095         /**
2096          * After any data loads/reads, the raw XML Document is available for further custom processing.
2097          * @type XMLDocument
2098          */
2099         this.xmlData = doc;
2100         var root = doc.documentElement || doc;
2101         var q = Roo.DomQuery;
2102         var recordType = this.recordType, fields = recordType.prototype.fields;
2103         var sid = this.meta.id;
2104         var totalRecords = 0, success = true;
2105         if(this.meta.totalRecords){
2106             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
2107         }
2108         
2109         if(this.meta.success){
2110             var sv = q.selectValue(this.meta.success, root, true);
2111             success = sv !== false && sv !== 'false';
2112         }
2113         var records = [];
2114         var ns = q.select(this.meta.record, root);
2115         for(var i = 0, len = ns.length; i < len; i++) {
2116                 var n = ns[i];
2117                 var values = {};
2118                 var id = sid ? q.selectValue(sid, n) : undefined;
2119                 for(var j = 0, jlen = fields.length; j < jlen; j++){
2120                     var f = fields.items[j];
2121                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
2122                     v = f.convert(v);
2123                     values[f.name] = v;
2124                 }
2125                 var record = new recordType(values, id);
2126                 record.node = n;
2127                 records[records.length] = record;
2128             }
2129
2130             return {
2131                 success : success,
2132                 records : records,
2133                 totalRecords : totalRecords || records.length
2134             };
2135     }
2136 });/*
2137  * Based on:
2138  * Ext JS Library 1.1.1
2139  * Copyright(c) 2006-2007, Ext JS, LLC.
2140  *
2141  * Originally Released Under LGPL - original licence link has changed is not relivant.
2142  *
2143  * Fork - LGPL
2144  * <script type="text/javascript">
2145  */
2146
2147 /**
2148  * @class Roo.data.ArrayReader
2149  * @extends Roo.data.DataReader
2150  * Data reader class to create an Array of Roo.data.Record objects from an Array.
2151  * Each element of that Array represents a row of data fields. The
2152  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
2153  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
2154  * <p>
2155  * Example code:.
2156  * <pre><code>
2157 var RecordDef = Roo.data.Record.create([
2158     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
2159     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
2160 ]);
2161 var myReader = new Roo.data.ArrayReader({
2162     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
2163 }, RecordDef);
2164 </code></pre>
2165  * <p>
2166  * This would consume an Array like this:
2167  * <pre><code>
2168 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
2169   </code></pre>
2170  
2171  * @constructor
2172  * Create a new JsonReader
2173  * @param {Object} meta Metadata configuration options.
2174  * @param {Object|Array} recordType Either an Array of field definition objects
2175  * 
2176  * @cfg {Array} fields Array of field definition objects
2177  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
2178  * as specified to {@link Roo.data.Record#create},
2179  * or an {@link Roo.data.Record} object
2180  *
2181  * 
2182  * created using {@link Roo.data.Record#create}.
2183  */
2184 Roo.data.ArrayReader = function(meta, recordType)
2185 {    
2186     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
2187 };
2188
2189 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
2190     
2191       /**
2192      * Create a data block containing Roo.data.Records from an XML document.
2193      * @param {Object} o An Array of row objects which represents the dataset.
2194      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
2195      * a cache of Roo.data.Records.
2196      */
2197     readRecords : function(o)
2198     {
2199         var sid = this.meta ? this.meta.id : null;
2200         var recordType = this.recordType, fields = recordType.prototype.fields;
2201         var records = [];
2202         var root = o;
2203         for(var i = 0; i < root.length; i++){
2204                 var n = root[i];
2205             var values = {};
2206             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
2207             for(var j = 0, jlen = fields.length; j < jlen; j++){
2208                 var f = fields.items[j];
2209                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
2210                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
2211                 v = f.convert(v);
2212                 values[f.name] = v;
2213             }
2214             var record = new recordType(values, id);
2215             record.json = n;
2216             records[records.length] = record;
2217         }
2218         return {
2219             records : records,
2220             totalRecords : records.length
2221         };
2222     },
2223     // used when loading children.. @see loadDataFromChildren
2224     toLoadData: function(rec)
2225     {
2226         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
2227         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
2228         
2229     }
2230     
2231     
2232 });/*
2233  * Based on:
2234  * Ext JS Library 1.1.1
2235  * Copyright(c) 2006-2007, Ext JS, LLC.
2236  *
2237  * Originally Released Under LGPL - original licence link has changed is not relivant.
2238  *
2239  * Fork - LGPL
2240  * <script type="text/javascript">
2241  */
2242
2243
2244 /**
2245  * @class Roo.data.Tree
2246  * @extends Roo.util.Observable
2247  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
2248  * in the tree have most standard DOM functionality.
2249  * @constructor
2250  * @param {Node} root (optional) The root node
2251  */
2252 Roo.data.Tree = function(root){
2253    this.nodeHash = {};
2254    /**
2255     * The root node for this tree
2256     * @type Node
2257     */
2258    this.root = null;
2259    if(root){
2260        this.setRootNode(root);
2261    }
2262    this.addEvents({
2263        /**
2264         * @event append
2265         * Fires when a new child node is appended to a node in this tree.
2266         * @param {Tree} tree The owner tree
2267         * @param {Node} parent The parent node
2268         * @param {Node} node The newly appended node
2269         * @param {Number} index The index of the newly appended node
2270         */
2271        "append" : true,
2272        /**
2273         * @event remove
2274         * Fires when a child node is removed from a node in this tree.
2275         * @param {Tree} tree The owner tree
2276         * @param {Node} parent The parent node
2277         * @param {Node} node The child node removed
2278         */
2279        "remove" : true,
2280        /**
2281         * @event move
2282         * Fires when a node is moved to a new location in the tree
2283         * @param {Tree} tree The owner tree
2284         * @param {Node} node The node moved
2285         * @param {Node} oldParent The old parent of this node
2286         * @param {Node} newParent The new parent of this node
2287         * @param {Number} index The index it was moved to
2288         */
2289        "move" : true,
2290        /**
2291         * @event insert
2292         * Fires when a new child node is inserted in a node in this tree.
2293         * @param {Tree} tree The owner tree
2294         * @param {Node} parent The parent node
2295         * @param {Node} node The child node inserted
2296         * @param {Node} refNode The child node the node was inserted before
2297         */
2298        "insert" : true,
2299        /**
2300         * @event beforeappend
2301         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
2302         * @param {Tree} tree The owner tree
2303         * @param {Node} parent The parent node
2304         * @param {Node} node The child node to be appended
2305         */
2306        "beforeappend" : true,
2307        /**
2308         * @event beforeremove
2309         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
2310         * @param {Tree} tree The owner tree
2311         * @param {Node} parent The parent node
2312         * @param {Node} node The child node to be removed
2313         */
2314        "beforeremove" : true,
2315        /**
2316         * @event beforemove
2317         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
2318         * @param {Tree} tree The owner tree
2319         * @param {Node} node The node being moved
2320         * @param {Node} oldParent The parent of the node
2321         * @param {Node} newParent The new parent the node is moving to
2322         * @param {Number} index The index it is being moved to
2323         */
2324        "beforemove" : true,
2325        /**
2326         * @event beforeinsert
2327         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
2328         * @param {Tree} tree The owner tree
2329         * @param {Node} parent The parent node
2330         * @param {Node} node The child node to be inserted
2331         * @param {Node} refNode The child node the node is being inserted before
2332         */
2333        "beforeinsert" : true
2334    });
2335
2336     Roo.data.Tree.superclass.constructor.call(this);
2337 };
2338
2339 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
2340     pathSeparator: "/",
2341
2342     proxyNodeEvent : function(){
2343         return this.fireEvent.apply(this, arguments);
2344     },
2345
2346     /**
2347      * Returns the root node for this tree.
2348      * @return {Node}
2349      */
2350     getRootNode : function(){
2351         return this.root;
2352     },
2353
2354     /**
2355      * Sets the root node for this tree.
2356      * @param {Node} node
2357      * @return {Node}
2358      */
2359     setRootNode : function(node){
2360         this.root = node;
2361         node.ownerTree = this;
2362         node.isRoot = true;
2363         this.registerNode(node);
2364         return node;
2365     },
2366
2367     /**
2368      * Gets a node in this tree by its id.
2369      * @param {String} id
2370      * @return {Node}
2371      */
2372     getNodeById : function(id){
2373         return this.nodeHash[id];
2374     },
2375
2376     registerNode : function(node){
2377         this.nodeHash[node.id] = node;
2378     },
2379
2380     unregisterNode : function(node){
2381         delete this.nodeHash[node.id];
2382     },
2383
2384     toString : function(){
2385         return "[Tree"+(this.id?" "+this.id:"")+"]";
2386     }
2387 });
2388
2389 /**
2390  * @class Roo.data.Node
2391  * @extends Roo.util.Observable
2392  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
2393  * @cfg {String} id The id for this node. If one is not specified, one is generated.
2394  * @constructor
2395  * @param {Object} attributes The attributes/config for the node
2396  */
2397 Roo.data.Node = function(attributes){
2398     /**
2399      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
2400      * @type {Object}
2401      */
2402     this.attributes = attributes || {};
2403     this.leaf = this.attributes.leaf;
2404     /**
2405      * The node id. @type String
2406      */
2407     this.id = this.attributes.id;
2408     if(!this.id){
2409         this.id = Roo.id(null, "ynode-");
2410         this.attributes.id = this.id;
2411     }
2412      
2413     
2414     /**
2415      * All child nodes of this node. @type Array
2416      */
2417     this.childNodes = [];
2418     if(!this.childNodes.indexOf){ // indexOf is a must
2419         this.childNodes.indexOf = function(o){
2420             for(var i = 0, len = this.length; i < len; i++){
2421                 if(this[i] == o) {
2422                     return i;
2423                 }
2424             }
2425             return -1;
2426         };
2427     }
2428     /**
2429      * The parent node for this node. @type Node
2430      */
2431     this.parentNode = null;
2432     /**
2433      * The first direct child node of this node, or null if this node has no child nodes. @type Node
2434      */
2435     this.firstChild = null;
2436     /**
2437      * The last direct child node of this node, or null if this node has no child nodes. @type Node
2438      */
2439     this.lastChild = null;
2440     /**
2441      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
2442      */
2443     this.previousSibling = null;
2444     /**
2445      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
2446      */
2447     this.nextSibling = null;
2448
2449     this.addEvents({
2450        /**
2451         * @event append
2452         * Fires when a new child node is appended
2453         * @param {Tree} tree The owner tree
2454         * @param {Node} this This node
2455         * @param {Node} node The newly appended node
2456         * @param {Number} index The index of the newly appended node
2457         */
2458        "append" : true,
2459        /**
2460         * @event remove
2461         * Fires when a child node is removed
2462         * @param {Tree} tree The owner tree
2463         * @param {Node} this This node
2464         * @param {Node} node The removed node
2465         */
2466        "remove" : true,
2467        /**
2468         * @event move
2469         * Fires when this node is moved to a new location in the tree
2470         * @param {Tree} tree The owner tree
2471         * @param {Node} this This node
2472         * @param {Node} oldParent The old parent of this node
2473         * @param {Node} newParent The new parent of this node
2474         * @param {Number} index The index it was moved to
2475         */
2476        "move" : true,
2477        /**
2478         * @event insert
2479         * Fires when a new child node is inserted.
2480         * @param {Tree} tree The owner tree
2481         * @param {Node} this This node
2482         * @param {Node} node The child node inserted
2483         * @param {Node} refNode The child node the node was inserted before
2484         */
2485        "insert" : true,
2486        /**
2487         * @event beforeappend
2488         * Fires before a new child is appended, return false to cancel the append.
2489         * @param {Tree} tree The owner tree
2490         * @param {Node} this This node
2491         * @param {Node} node The child node to be appended
2492         */
2493        "beforeappend" : true,
2494        /**
2495         * @event beforeremove
2496         * Fires before a child is removed, return false to cancel the remove.
2497         * @param {Tree} tree The owner tree
2498         * @param {Node} this This node
2499         * @param {Node} node The child node to be removed
2500         */
2501        "beforeremove" : true,
2502        /**
2503         * @event beforemove
2504         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
2505         * @param {Tree} tree The owner tree
2506         * @param {Node} this This node
2507         * @param {Node} oldParent The parent of this node
2508         * @param {Node} newParent The new parent this node is moving to
2509         * @param {Number} index The index it is being moved to
2510         */
2511        "beforemove" : true,
2512        /**
2513         * @event beforeinsert
2514         * Fires before a new child is inserted, return false to cancel the insert.
2515         * @param {Tree} tree The owner tree
2516         * @param {Node} this This node
2517         * @param {Node} node The child node to be inserted
2518         * @param {Node} refNode The child node the node is being inserted before
2519         */
2520        "beforeinsert" : true
2521    });
2522     this.listeners = this.attributes.listeners;
2523     Roo.data.Node.superclass.constructor.call(this);
2524 };
2525
2526 Roo.extend(Roo.data.Node, Roo.util.Observable, {
2527     fireEvent : function(evtName){
2528         // first do standard event for this node
2529         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
2530             return false;
2531         }
2532         // then bubble it up to the tree if the event wasn't cancelled
2533         var ot = this.getOwnerTree();
2534         if(ot){
2535             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
2536                 return false;
2537             }
2538         }
2539         return true;
2540     },
2541
2542     /**
2543      * Returns true if this node is a leaf
2544      * @return {Boolean}
2545      */
2546     isLeaf : function(){
2547         return this.leaf === true;
2548     },
2549
2550     // private
2551     setFirstChild : function(node){
2552         this.firstChild = node;
2553     },
2554
2555     //private
2556     setLastChild : function(node){
2557         this.lastChild = node;
2558     },
2559
2560
2561     /**
2562      * Returns true if this node is the last child of its parent
2563      * @return {Boolean}
2564      */
2565     isLast : function(){
2566        return (!this.parentNode ? true : this.parentNode.lastChild == this);
2567     },
2568
2569     /**
2570      * Returns true if this node is the first child of its parent
2571      * @return {Boolean}
2572      */
2573     isFirst : function(){
2574        return (!this.parentNode ? true : this.parentNode.firstChild == this);
2575     },
2576
2577     hasChildNodes : function(){
2578         return !this.isLeaf() && this.childNodes.length > 0;
2579     },
2580
2581     /**
2582      * Insert node(s) as the last child node of this node.
2583      * @param {Node/Array} node The node or Array of nodes to append
2584      * @return {Node} The appended node if single append, or null if an array was passed
2585      */
2586     appendChild : function(node){
2587         var multi = false;
2588         if(node instanceof Array){
2589             multi = node;
2590         }else if(arguments.length > 1){
2591             multi = arguments;
2592         }
2593         
2594         // if passed an array or multiple args do them one by one
2595         if(multi){
2596             for(var i = 0, len = multi.length; i < len; i++) {
2597                 this.appendChild(multi[i]);
2598             }
2599         }else{
2600             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
2601                 return false;
2602             }
2603             var index = this.childNodes.length;
2604             var oldParent = node.parentNode;
2605             // it's a move, make sure we move it cleanly
2606             if(oldParent){
2607                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
2608                     return false;
2609                 }
2610                 oldParent.removeChild(node);
2611             }
2612             
2613             index = this.childNodes.length;
2614             if(index == 0){
2615                 this.setFirstChild(node);
2616             }
2617             this.childNodes.push(node);
2618             node.parentNode = this;
2619             var ps = this.childNodes[index-1];
2620             if(ps){
2621                 node.previousSibling = ps;
2622                 ps.nextSibling = node;
2623             }else{
2624                 node.previousSibling = null;
2625             }
2626             node.nextSibling = null;
2627             this.setLastChild(node);
2628             node.setOwnerTree(this.getOwnerTree());
2629             this.fireEvent("append", this.ownerTree, this, node, index);
2630             if(this.ownerTree) {
2631                 this.ownerTree.fireEvent("appendnode", this, node, index);
2632             }
2633             if(oldParent){
2634                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
2635             }
2636             return node;
2637         }
2638     },
2639
2640     /**
2641      * Removes a child node from this node.
2642      * @param {Node} node The node to remove
2643      * @return {Node} The removed node
2644      */
2645     removeChild : function(node){
2646         var index = this.childNodes.indexOf(node);
2647         if(index == -1){
2648             return false;
2649         }
2650         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
2651             return false;
2652         }
2653
2654         // remove it from childNodes collection
2655         this.childNodes.splice(index, 1);
2656
2657         // update siblings
2658         if(node.previousSibling){
2659             node.previousSibling.nextSibling = node.nextSibling;
2660         }
2661         if(node.nextSibling){
2662             node.nextSibling.previousSibling = node.previousSibling;
2663         }
2664
2665         // update child refs
2666         if(this.firstChild == node){
2667             this.setFirstChild(node.nextSibling);
2668         }
2669         if(this.lastChild == node){
2670             this.setLastChild(node.previousSibling);
2671         }
2672
2673         node.setOwnerTree(null);
2674         // clear any references from the node
2675         node.parentNode = null;
2676         node.previousSibling = null;
2677         node.nextSibling = null;
2678         this.fireEvent("remove", this.ownerTree, this, node);
2679         return node;
2680     },
2681
2682     /**
2683      * Inserts the first node before the second node in this nodes childNodes collection.
2684      * @param {Node} node The node to insert
2685      * @param {Node} refNode The node to insert before (if null the node is appended)
2686      * @return {Node} The inserted node
2687      */
2688     insertBefore : function(node, refNode){
2689         if(!refNode){ // like standard Dom, refNode can be null for append
2690             return this.appendChild(node);
2691         }
2692         // nothing to do
2693         if(node == refNode){
2694             return false;
2695         }
2696
2697         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
2698             return false;
2699         }
2700         var index = this.childNodes.indexOf(refNode);
2701         var oldParent = node.parentNode;
2702         var refIndex = index;
2703
2704         // when moving internally, indexes will change after remove
2705         if(oldParent == this && this.childNodes.indexOf(node) < index){
2706             refIndex--;
2707         }
2708
2709         // it's a move, make sure we move it cleanly
2710         if(oldParent){
2711             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
2712                 return false;
2713             }
2714             oldParent.removeChild(node);
2715         }
2716         if(refIndex == 0){
2717             this.setFirstChild(node);
2718         }
2719         this.childNodes.splice(refIndex, 0, node);
2720         node.parentNode = this;
2721         var ps = this.childNodes[refIndex-1];
2722         if(ps){
2723             node.previousSibling = ps;
2724             ps.nextSibling = node;
2725         }else{
2726             node.previousSibling = null;
2727         }
2728         node.nextSibling = refNode;
2729         refNode.previousSibling = node;
2730         node.setOwnerTree(this.getOwnerTree());
2731         this.fireEvent("insert", this.ownerTree, this, node, refNode);
2732         if(oldParent){
2733             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
2734         }
2735         return node;
2736     },
2737
2738     /**
2739      * Returns the child node at the specified index.
2740      * @param {Number} index
2741      * @return {Node}
2742      */
2743     item : function(index){
2744         return this.childNodes[index];
2745     },
2746
2747     /**
2748      * Replaces one child node in this node with another.
2749      * @param {Node} newChild The replacement node
2750      * @param {Node} oldChild The node to replace
2751      * @return {Node} The replaced node
2752      */
2753     replaceChild : function(newChild, oldChild){
2754         this.insertBefore(newChild, oldChild);
2755         this.removeChild(oldChild);
2756         return oldChild;
2757     },
2758
2759     /**
2760      * Returns the index of a child node
2761      * @param {Node} node
2762      * @return {Number} The index of the node or -1 if it was not found
2763      */
2764     indexOf : function(child){
2765         return this.childNodes.indexOf(child);
2766     },
2767
2768     /**
2769      * Returns the tree this node is in.
2770      * @return {Tree}
2771      */
2772     getOwnerTree : function(){
2773         // if it doesn't have one, look for one
2774         if(!this.ownerTree){
2775             var p = this;
2776             while(p){
2777                 if(p.ownerTree){
2778                     this.ownerTree = p.ownerTree;
2779                     break;
2780                 }
2781                 p = p.parentNode;
2782             }
2783         }
2784         return this.ownerTree;
2785     },
2786
2787     /**
2788      * Returns depth of this node (the root node has a depth of 0)
2789      * @return {Number}
2790      */
2791     getDepth : function(){
2792         var depth = 0;
2793         var p = this;
2794         while(p.parentNode){
2795             ++depth;
2796             p = p.parentNode;
2797         }
2798         return depth;
2799     },
2800
2801     // private
2802     setOwnerTree : function(tree){
2803         // if it's move, we need to update everyone
2804         if(tree != this.ownerTree){
2805             if(this.ownerTree){
2806                 this.ownerTree.unregisterNode(this);
2807             }
2808             this.ownerTree = tree;
2809             var cs = this.childNodes;
2810             for(var i = 0, len = cs.length; i < len; i++) {
2811                 cs[i].setOwnerTree(tree);
2812             }
2813             if(tree){
2814                 tree.registerNode(this);
2815             }
2816         }
2817     },
2818
2819     /**
2820      * Returns the path for this node. The path can be used to expand or select this node programmatically.
2821      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
2822      * @return {String} The path
2823      */
2824     getPath : function(attr){
2825         attr = attr || "id";
2826         var p = this.parentNode;
2827         var b = [this.attributes[attr]];
2828         while(p){
2829             b.unshift(p.attributes[attr]);
2830             p = p.parentNode;
2831         }
2832         var sep = this.getOwnerTree().pathSeparator;
2833         return sep + b.join(sep);
2834     },
2835
2836     /**
2837      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2838      * function call will be the scope provided or the current node. The arguments to the function
2839      * will be the args provided or the current node. If the function returns false at any point,
2840      * the bubble is stopped.
2841      * @param {Function} fn The function to call
2842      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2843      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2844      */
2845     bubble : function(fn, scope, args){
2846         var p = this;
2847         while(p){
2848             if(fn.call(scope || p, args || p) === false){
2849                 break;
2850             }
2851             p = p.parentNode;
2852         }
2853     },
2854
2855     /**
2856      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
2857      * function call will be the scope provided or the current node. The arguments to the function
2858      * will be the args provided or the current node. If the function returns false at any point,
2859      * the cascade is stopped on that branch.
2860      * @param {Function} fn The function to call
2861      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2862      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2863      */
2864     cascade : function(fn, scope, args){
2865         if(fn.call(scope || this, args || this) !== false){
2866             var cs = this.childNodes;
2867             for(var i = 0, len = cs.length; i < len; i++) {
2868                 cs[i].cascade(fn, scope, args);
2869             }
2870         }
2871     },
2872
2873     /**
2874      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
2875      * function call will be the scope provided or the current node. The arguments to the function
2876      * will be the args provided or the current node. If the function returns false at any point,
2877      * the iteration stops.
2878      * @param {Function} fn The function to call
2879      * @param {Object} scope (optional) The scope of the function (defaults to current node)
2880      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
2881      */
2882     eachChild : function(fn, scope, args){
2883         var cs = this.childNodes;
2884         for(var i = 0, len = cs.length; i < len; i++) {
2885                 if(fn.call(scope || this, args || cs[i]) === false){
2886                     break;
2887                 }
2888         }
2889     },
2890
2891     /**
2892      * Finds the first child that has the attribute with the specified value.
2893      * @param {String} attribute The attribute name
2894      * @param {Mixed} value The value to search for
2895      * @return {Node} The found child or null if none was found
2896      */
2897     findChild : function(attribute, value){
2898         var cs = this.childNodes;
2899         for(var i = 0, len = cs.length; i < len; i++) {
2900                 if(cs[i].attributes[attribute] == value){
2901                     return cs[i];
2902                 }
2903         }
2904         return null;
2905     },
2906
2907     /**
2908      * Finds the first child by a custom function. The child matches if the function passed
2909      * returns true.
2910      * @param {Function} fn
2911      * @param {Object} scope (optional)
2912      * @return {Node} The found child or null if none was found
2913      */
2914     findChildBy : function(fn, scope){
2915         var cs = this.childNodes;
2916         for(var i = 0, len = cs.length; i < len; i++) {
2917                 if(fn.call(scope||cs[i], cs[i]) === true){
2918                     return cs[i];
2919                 }
2920         }
2921         return null;
2922     },
2923
2924     /**
2925      * Sorts this nodes children using the supplied sort function
2926      * @param {Function} fn
2927      * @param {Object} scope (optional)
2928      */
2929     sort : function(fn, scope){
2930         var cs = this.childNodes;
2931         var len = cs.length;
2932         if(len > 0){
2933             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
2934             cs.sort(sortFn);
2935             for(var i = 0; i < len; i++){
2936                 var n = cs[i];
2937                 n.previousSibling = cs[i-1];
2938                 n.nextSibling = cs[i+1];
2939                 if(i == 0){
2940                     this.setFirstChild(n);
2941                 }
2942                 if(i == len-1){
2943                     this.setLastChild(n);
2944                 }
2945             }
2946         }
2947     },
2948
2949     /**
2950      * Returns true if this node is an ancestor (at any point) of the passed node.
2951      * @param {Node} node
2952      * @return {Boolean}
2953      */
2954     contains : function(node){
2955         return node.isAncestor(this);
2956     },
2957
2958     /**
2959      * Returns true if the passed node is an ancestor (at any point) of this node.
2960      * @param {Node} node
2961      * @return {Boolean}
2962      */
2963     isAncestor : function(node){
2964         var p = this.parentNode;
2965         while(p){
2966             if(p == node){
2967                 return true;
2968             }
2969             p = p.parentNode;
2970         }
2971         return false;
2972     },
2973
2974     toString : function(){
2975         return "[Node"+(this.id?" "+this.id:"")+"]";
2976     }
2977 });/*
2978  * Based on:
2979  * Ext JS Library 1.1.1
2980  * Copyright(c) 2006-2007, Ext JS, LLC.
2981  *
2982  * Originally Released Under LGPL - original licence link has changed is not relivant.
2983  *
2984  * Fork - LGPL
2985  * <script type="text/javascript">
2986  */
2987
2988
2989 /**
2990  * @class Roo.Shadow
2991  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
2992  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
2993  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
2994  * @constructor
2995  * Create a new Shadow
2996  * @param {Object} config The config object
2997  */
2998 Roo.Shadow = function(config){
2999     Roo.apply(this, config);
3000     if(typeof this.mode != "string"){
3001         this.mode = this.defaultMode;
3002     }
3003     var o = this.offset, a = {h: 0};
3004     var rad = Math.floor(this.offset/2);
3005     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
3006         case "drop":
3007             a.w = 0;
3008             a.l = a.t = o;
3009             a.t -= 1;
3010             if(Roo.isIE){
3011                 a.l -= this.offset + rad;
3012                 a.t -= this.offset + rad;
3013                 a.w -= rad;
3014                 a.h -= rad;
3015                 a.t += 1;
3016             }
3017         break;
3018         case "sides":
3019             a.w = (o*2);
3020             a.l = -o;
3021             a.t = o-1;
3022             if(Roo.isIE){
3023                 a.l -= (this.offset - rad);
3024                 a.t -= this.offset + rad;
3025                 a.l += 1;
3026                 a.w -= (this.offset - rad)*2;
3027                 a.w -= rad + 1;
3028                 a.h -= 1;
3029             }
3030         break;
3031         case "frame":
3032             a.w = a.h = (o*2);
3033             a.l = a.t = -o;
3034             a.t += 1;
3035             a.h -= 2;
3036             if(Roo.isIE){
3037                 a.l -= (this.offset - rad);
3038                 a.t -= (this.offset - rad);
3039                 a.l += 1;
3040                 a.w -= (this.offset + rad + 1);
3041                 a.h -= (this.offset + rad);
3042                 a.h += 1;
3043             }
3044         break;
3045     };
3046
3047     this.adjusts = a;
3048 };
3049
3050 Roo.Shadow.prototype = {
3051     /**
3052      * @cfg {String} mode
3053      * The shadow display mode.  Supports the following options:<br />
3054      * sides: Shadow displays on both sides and bottom only<br />
3055      * frame: Shadow displays equally on all four sides<br />
3056      * drop: Traditional bottom-right drop shadow (default)
3057      */
3058     /**
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             st = '<style type="text/css">' +
20658                     this.stylesheets +
20659                 '</style>';
20660         }
20661         
20662         st +=  '<style type="text/css">' +
20663             'IMG { cursor: pointer } ' +
20664         '</style>';
20665
20666         var cls = 'roo-htmleditor-body';
20667         
20668         if(this.bodyCls.length){
20669             cls += ' ' + this.bodyCls;
20670         }
20671         
20672         return '<html><head>' + st  +
20673             //<style type="text/css">' +
20674             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20675             //'</style>' +
20676             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
20677     },
20678
20679     // private
20680     onRender : function(ct, position)
20681     {
20682         var _t = this;
20683         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20684         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20685         
20686         
20687         this.el.dom.style.border = '0 none';
20688         this.el.dom.setAttribute('tabIndex', -1);
20689         this.el.addClass('x-hidden hide');
20690         
20691         
20692         
20693         if(Roo.isIE){ // fix IE 1px bogus margin
20694             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20695         }
20696        
20697         
20698         this.frameId = Roo.id();
20699         
20700          
20701         
20702         var iframe = this.owner.wrap.createChild({
20703             tag: 'iframe',
20704             cls: 'form-control', // bootstrap..
20705             id: this.frameId,
20706             name: this.frameId,
20707             frameBorder : 'no',
20708             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
20709         }, this.el
20710         );
20711         
20712         
20713         this.iframe = iframe.dom;
20714
20715          this.assignDocWin();
20716         
20717         this.doc.designMode = 'on';
20718        
20719         this.doc.open();
20720         this.doc.write(this.getDocMarkup());
20721         this.doc.close();
20722
20723         
20724         var task = { // must defer to wait for browser to be ready
20725             run : function(){
20726                 //console.log("run task?" + this.doc.readyState);
20727                 this.assignDocWin();
20728                 if(this.doc.body || this.doc.readyState == 'complete'){
20729                     try {
20730                         this.doc.designMode="on";
20731                     } catch (e) {
20732                         return;
20733                     }
20734                     Roo.TaskMgr.stop(task);
20735                     this.initEditor.defer(10, this);
20736                 }
20737             },
20738             interval : 10,
20739             duration: 10000,
20740             scope: this
20741         };
20742         Roo.TaskMgr.start(task);
20743
20744     },
20745
20746     // private
20747     onResize : function(w, h)
20748     {
20749          Roo.log('resize: ' +w + ',' + h );
20750         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20751         if(!this.iframe){
20752             return;
20753         }
20754         if(typeof w == 'number'){
20755             
20756             this.iframe.style.width = w + 'px';
20757         }
20758         if(typeof h == 'number'){
20759             
20760             this.iframe.style.height = h + 'px';
20761             if(this.doc){
20762                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20763             }
20764         }
20765         
20766     },
20767
20768     /**
20769      * Toggles the editor between standard and source edit mode.
20770      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20771      */
20772     toggleSourceEdit : function(sourceEditMode){
20773         
20774         this.sourceEditMode = sourceEditMode === true;
20775         
20776         if(this.sourceEditMode){
20777  
20778             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
20779             
20780         }else{
20781             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20782             //this.iframe.className = '';
20783             this.deferFocus();
20784         }
20785         //this.setSize(this.owner.wrap.getSize());
20786         //this.fireEvent('editmodechange', this, this.sourceEditMode);
20787     },
20788
20789     
20790   
20791
20792     /**
20793      * Protected method that will not generally be called directly. If you need/want
20794      * custom HTML cleanup, this is the method you should override.
20795      * @param {String} html The HTML to be cleaned
20796      * return {String} The cleaned HTML
20797      */
20798     cleanHtml : function(html){
20799         html = String(html);
20800         if(html.length > 5){
20801             if(Roo.isSafari){ // strip safari nonsense
20802                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20803             }
20804         }
20805         if(html == '&nbsp;'){
20806             html = '';
20807         }
20808         return html;
20809     },
20810
20811     /**
20812      * HTML Editor -> Textarea
20813      * Protected method that will not generally be called directly. Syncs the contents
20814      * of the editor iframe with the textarea.
20815      */
20816     syncValue : function(){
20817         if(this.initialized){
20818             var bd = (this.doc.body || this.doc.documentElement);
20819             //this.cleanUpPaste(); -- this is done else where and causes havoc..
20820             var html = bd.innerHTML;
20821             if(Roo.isSafari){
20822                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20823                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20824                 if(m && m[1]){
20825                     html = '<div style="'+m[0]+'">' + html + '</div>';
20826                 }
20827             }
20828             html = this.cleanHtml(html);
20829             // fix up the special chars.. normaly like back quotes in word...
20830             // however we do not want to do this with chinese..
20831             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
20832                 
20833                 var cc = match.charCodeAt();
20834
20835                 // Get the character value, handling surrogate pairs
20836                 if (match.length == 2) {
20837                     // It's a surrogate pair, calculate the Unicode code point
20838                     var high = match.charCodeAt(0) - 0xD800;
20839                     var low  = match.charCodeAt(1) - 0xDC00;
20840                     cc = (high * 0x400) + low + 0x10000;
20841                 }  else if (
20842                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20843                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20844                     (cc >= 0xf900 && cc < 0xfb00 )
20845                 ) {
20846                         return match;
20847                 }  
20848          
20849                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
20850                 return "&#" + cc + ";";
20851                 
20852                 
20853             });
20854             
20855             
20856              
20857             if(this.owner.fireEvent('beforesync', this, html) !== false){
20858                 this.el.dom.value = html;
20859                 this.owner.fireEvent('sync', this, html);
20860             }
20861         }
20862     },
20863
20864     /**
20865      * Protected method that will not generally be called directly. Pushes the value of the textarea
20866      * into the iframe editor.
20867      */
20868     pushValue : function(){
20869         if(this.initialized){
20870             var v = this.el.dom.value.trim();
20871             
20872 //            if(v.length < 1){
20873 //                v = '&#160;';
20874 //            }
20875             
20876             if(this.owner.fireEvent('beforepush', this, v) !== false){
20877                 var d = (this.doc.body || this.doc.documentElement);
20878                 d.innerHTML = v;
20879                 this.cleanUpPaste();
20880                 this.el.dom.value = d.innerHTML;
20881                 this.owner.fireEvent('push', this, v);
20882             }
20883         }
20884     },
20885
20886     // private
20887     deferFocus : function(){
20888         this.focus.defer(10, this);
20889     },
20890
20891     // doc'ed in Field
20892     focus : function(){
20893         if(this.win && !this.sourceEditMode){
20894             this.win.focus();
20895         }else{
20896             this.el.focus();
20897         }
20898     },
20899     
20900     assignDocWin: function()
20901     {
20902         var iframe = this.iframe;
20903         
20904          if(Roo.isIE){
20905             this.doc = iframe.contentWindow.document;
20906             this.win = iframe.contentWindow;
20907         } else {
20908 //            if (!Roo.get(this.frameId)) {
20909 //                return;
20910 //            }
20911 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20912 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20913             
20914             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20915                 return;
20916             }
20917             
20918             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20919             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20920         }
20921     },
20922     
20923     // private
20924     initEditor : function(){
20925         //console.log("INIT EDITOR");
20926         this.assignDocWin();
20927         
20928         
20929         
20930         this.doc.designMode="on";
20931         this.doc.open();
20932         this.doc.write(this.getDocMarkup());
20933         this.doc.close();
20934         
20935         var dbody = (this.doc.body || this.doc.documentElement);
20936         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20937         // this copies styles from the containing element into thsi one..
20938         // not sure why we need all of this..
20939         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20940         
20941         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20942         //ss['background-attachment'] = 'fixed'; // w3c
20943         dbody.bgProperties = 'fixed'; // ie
20944         //Roo.DomHelper.applyStyles(dbody, ss);
20945         Roo.EventManager.on(this.doc, {
20946             //'mousedown': this.onEditorEvent,
20947             'mouseup': this.onEditorEvent,
20948             'dblclick': this.onEditorEvent,
20949             'click': this.onEditorEvent,
20950             'keyup': this.onEditorEvent,
20951             buffer:100,
20952             scope: this
20953         });
20954         if(Roo.isGecko){
20955             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20956         }
20957         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20958             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20959         }
20960         this.initialized = true;
20961
20962         this.owner.fireEvent('initialize', this);
20963         this.pushValue();
20964     },
20965
20966     // private
20967     onDestroy : function(){
20968         
20969         
20970         
20971         if(this.rendered){
20972             
20973             //for (var i =0; i < this.toolbars.length;i++) {
20974             //    // fixme - ask toolbars for heights?
20975             //    this.toolbars[i].onDestroy();
20976            // }
20977             
20978             //this.wrap.dom.innerHTML = '';
20979             //this.wrap.remove();
20980         }
20981     },
20982
20983     // private
20984     onFirstFocus : function(){
20985         
20986         this.assignDocWin();
20987         
20988         
20989         this.activated = true;
20990          
20991     
20992         if(Roo.isGecko){ // prevent silly gecko errors
20993             this.win.focus();
20994             var s = this.win.getSelection();
20995             if(!s.focusNode || s.focusNode.nodeType != 3){
20996                 var r = s.getRangeAt(0);
20997                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20998                 r.collapse(true);
20999                 this.deferFocus();
21000             }
21001             try{
21002                 this.execCmd('useCSS', true);
21003                 this.execCmd('styleWithCSS', false);
21004             }catch(e){}
21005         }
21006         this.owner.fireEvent('activate', this);
21007     },
21008
21009     // private
21010     adjustFont: function(btn){
21011         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21012         //if(Roo.isSafari){ // safari
21013         //    adjust *= 2;
21014        // }
21015         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21016         if(Roo.isSafari){ // safari
21017             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21018             v =  (v < 10) ? 10 : v;
21019             v =  (v > 48) ? 48 : v;
21020             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21021             
21022         }
21023         
21024         
21025         v = Math.max(1, v+adjust);
21026         
21027         this.execCmd('FontSize', v  );
21028     },
21029
21030     onEditorEvent : function(e)
21031     {
21032         this.owner.fireEvent('editorevent', this, e);
21033       //  this.updateToolbar();
21034         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21035     },
21036
21037     insertTag : function(tg)
21038     {
21039         // could be a bit smarter... -> wrap the current selected tRoo..
21040         if (tg.toLowerCase() == 'span' ||
21041             tg.toLowerCase() == 'code' ||
21042             tg.toLowerCase() == 'sup' ||
21043             tg.toLowerCase() == 'sub' 
21044             ) {
21045             
21046             range = this.createRange(this.getSelection());
21047             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21048             wrappingNode.appendChild(range.extractContents());
21049             range.insertNode(wrappingNode);
21050
21051             return;
21052             
21053             
21054             
21055         }
21056         this.execCmd("formatblock",   tg);
21057         
21058     },
21059     
21060     insertText : function(txt)
21061     {
21062         
21063         
21064         var range = this.createRange();
21065         range.deleteContents();
21066                //alert(Sender.getAttribute('label'));
21067                
21068         range.insertNode(this.doc.createTextNode(txt));
21069     } ,
21070     
21071      
21072
21073     /**
21074      * Executes a Midas editor command on the editor document and performs necessary focus and
21075      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21076      * @param {String} cmd The Midas command
21077      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21078      */
21079     relayCmd : function(cmd, value){
21080         this.win.focus();
21081         this.execCmd(cmd, value);
21082         this.owner.fireEvent('editorevent', this);
21083         //this.updateToolbar();
21084         this.owner.deferFocus();
21085     },
21086
21087     /**
21088      * Executes a Midas editor command directly on the editor document.
21089      * For visual commands, you should use {@link #relayCmd} instead.
21090      * <b>This should only be called after the editor is initialized.</b>
21091      * @param {String} cmd The Midas command
21092      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21093      */
21094     execCmd : function(cmd, value){
21095         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21096         this.syncValue();
21097     },
21098  
21099  
21100    
21101     /**
21102      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21103      * to insert tRoo.
21104      * @param {String} text | dom node.. 
21105      */
21106     insertAtCursor : function(text)
21107     {
21108         
21109         if(!this.activated){
21110             return;
21111         }
21112         /*
21113         if(Roo.isIE){
21114             this.win.focus();
21115             var r = this.doc.selection.createRange();
21116             if(r){
21117                 r.collapse(true);
21118                 r.pasteHTML(text);
21119                 this.syncValue();
21120                 this.deferFocus();
21121             
21122             }
21123             return;
21124         }
21125         */
21126         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21127             this.win.focus();
21128             
21129             
21130             // from jquery ui (MIT licenced)
21131             var range, node;
21132             var win = this.win;
21133             
21134             if (win.getSelection && win.getSelection().getRangeAt) {
21135                 range = win.getSelection().getRangeAt(0);
21136                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21137                 range.insertNode(node);
21138             } else if (win.document.selection && win.document.selection.createRange) {
21139                 // no firefox support
21140                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21141                 win.document.selection.createRange().pasteHTML(txt);
21142             } else {
21143                 // no firefox support
21144                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21145                 this.execCmd('InsertHTML', txt);
21146             } 
21147             
21148             this.syncValue();
21149             
21150             this.deferFocus();
21151         }
21152     },
21153  // private
21154     mozKeyPress : function(e){
21155         if(e.ctrlKey){
21156             var c = e.getCharCode(), cmd;
21157           
21158             if(c > 0){
21159                 c = String.fromCharCode(c).toLowerCase();
21160                 switch(c){
21161                     case 'b':
21162                         cmd = 'bold';
21163                         break;
21164                     case 'i':
21165                         cmd = 'italic';
21166                         break;
21167                     
21168                     case 'u':
21169                         cmd = 'underline';
21170                         break;
21171                     
21172                     case 'v':
21173                         this.cleanUpPaste.defer(100, this);
21174                         return;
21175                         
21176                 }
21177                 if(cmd){
21178                     this.win.focus();
21179                     this.execCmd(cmd);
21180                     this.deferFocus();
21181                     e.preventDefault();
21182                 }
21183                 
21184             }
21185         }
21186     },
21187
21188     // private
21189     fixKeys : function(){ // load time branching for fastest keydown performance
21190         if(Roo.isIE){
21191             return function(e){
21192                 var k = e.getKey(), r;
21193                 if(k == e.TAB){
21194                     e.stopEvent();
21195                     r = this.doc.selection.createRange();
21196                     if(r){
21197                         r.collapse(true);
21198                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21199                         this.deferFocus();
21200                     }
21201                     return;
21202                 }
21203                 
21204                 if(k == e.ENTER){
21205                     r = this.doc.selection.createRange();
21206                     if(r){
21207                         var target = r.parentElement();
21208                         if(!target || target.tagName.toLowerCase() != 'li'){
21209                             e.stopEvent();
21210                             r.pasteHTML('<br />');
21211                             r.collapse(false);
21212                             r.select();
21213                         }
21214                     }
21215                 }
21216                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21217                     this.cleanUpPaste.defer(100, this);
21218                     return;
21219                 }
21220                 
21221                 
21222             };
21223         }else if(Roo.isOpera){
21224             return function(e){
21225                 var k = e.getKey();
21226                 if(k == e.TAB){
21227                     e.stopEvent();
21228                     this.win.focus();
21229                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21230                     this.deferFocus();
21231                 }
21232                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21233                     this.cleanUpPaste.defer(100, this);
21234                     return;
21235                 }
21236                 
21237             };
21238         }else if(Roo.isSafari){
21239             return function(e){
21240                 var k = e.getKey();
21241                 
21242                 if(k == e.TAB){
21243                     e.stopEvent();
21244                     this.execCmd('InsertText','\t');
21245                     this.deferFocus();
21246                     return;
21247                 }
21248                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21249                     this.cleanUpPaste.defer(100, this);
21250                     return;
21251                 }
21252                 
21253              };
21254         }
21255     }(),
21256     
21257     getAllAncestors: function()
21258     {
21259         var p = this.getSelectedNode();
21260         var a = [];
21261         if (!p) {
21262             a.push(p); // push blank onto stack..
21263             p = this.getParentElement();
21264         }
21265         
21266         
21267         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21268             a.push(p);
21269             p = p.parentNode;
21270         }
21271         a.push(this.doc.body);
21272         return a;
21273     },
21274     lastSel : false,
21275     lastSelNode : false,
21276     
21277     
21278     getSelection : function() 
21279     {
21280         this.assignDocWin();
21281         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21282     },
21283     
21284     getSelectedNode: function() 
21285     {
21286         // this may only work on Gecko!!!
21287         
21288         // should we cache this!!!!
21289         
21290         
21291         
21292          
21293         var range = this.createRange(this.getSelection()).cloneRange();
21294         
21295         if (Roo.isIE) {
21296             var parent = range.parentElement();
21297             while (true) {
21298                 var testRange = range.duplicate();
21299                 testRange.moveToElementText(parent);
21300                 if (testRange.inRange(range)) {
21301                     break;
21302                 }
21303                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21304                     break;
21305                 }
21306                 parent = parent.parentElement;
21307             }
21308             return parent;
21309         }
21310         
21311         // is ancestor a text element.
21312         var ac =  range.commonAncestorContainer;
21313         if (ac.nodeType == 3) {
21314             ac = ac.parentNode;
21315         }
21316         
21317         var ar = ac.childNodes;
21318          
21319         var nodes = [];
21320         var other_nodes = [];
21321         var has_other_nodes = false;
21322         for (var i=0;i<ar.length;i++) {
21323             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21324                 continue;
21325             }
21326             // fullly contained node.
21327             
21328             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21329                 nodes.push(ar[i]);
21330                 continue;
21331             }
21332             
21333             // probably selected..
21334             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21335                 other_nodes.push(ar[i]);
21336                 continue;
21337             }
21338             // outer..
21339             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21340                 continue;
21341             }
21342             
21343             
21344             has_other_nodes = true;
21345         }
21346         if (!nodes.length && other_nodes.length) {
21347             nodes= other_nodes;
21348         }
21349         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21350             return false;
21351         }
21352         
21353         return nodes[0];
21354     },
21355     createRange: function(sel)
21356     {
21357         // this has strange effects when using with 
21358         // top toolbar - not sure if it's a great idea.
21359         //this.editor.contentWindow.focus();
21360         if (typeof sel != "undefined") {
21361             try {
21362                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21363             } catch(e) {
21364                 return this.doc.createRange();
21365             }
21366         } else {
21367             return this.doc.createRange();
21368         }
21369     },
21370     getParentElement: function()
21371     {
21372         
21373         this.assignDocWin();
21374         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21375         
21376         var range = this.createRange(sel);
21377          
21378         try {
21379             var p = range.commonAncestorContainer;
21380             while (p.nodeType == 3) { // text node
21381                 p = p.parentNode;
21382             }
21383             return p;
21384         } catch (e) {
21385             return null;
21386         }
21387     
21388     },
21389     /***
21390      *
21391      * Range intersection.. the hard stuff...
21392      *  '-1' = before
21393      *  '0' = hits..
21394      *  '1' = after.
21395      *         [ -- selected range --- ]
21396      *   [fail]                        [fail]
21397      *
21398      *    basically..
21399      *      if end is before start or  hits it. fail.
21400      *      if start is after end or hits it fail.
21401      *
21402      *   if either hits (but other is outside. - then it's not 
21403      *   
21404      *    
21405      **/
21406     
21407     
21408     // @see http://www.thismuchiknow.co.uk/?p=64.
21409     rangeIntersectsNode : function(range, node)
21410     {
21411         var nodeRange = node.ownerDocument.createRange();
21412         try {
21413             nodeRange.selectNode(node);
21414         } catch (e) {
21415             nodeRange.selectNodeContents(node);
21416         }
21417     
21418         var rangeStartRange = range.cloneRange();
21419         rangeStartRange.collapse(true);
21420     
21421         var rangeEndRange = range.cloneRange();
21422         rangeEndRange.collapse(false);
21423     
21424         var nodeStartRange = nodeRange.cloneRange();
21425         nodeStartRange.collapse(true);
21426     
21427         var nodeEndRange = nodeRange.cloneRange();
21428         nodeEndRange.collapse(false);
21429     
21430         return rangeStartRange.compareBoundaryPoints(
21431                  Range.START_TO_START, nodeEndRange) == -1 &&
21432                rangeEndRange.compareBoundaryPoints(
21433                  Range.START_TO_START, nodeStartRange) == 1;
21434         
21435          
21436     },
21437     rangeCompareNode : function(range, node)
21438     {
21439         var nodeRange = node.ownerDocument.createRange();
21440         try {
21441             nodeRange.selectNode(node);
21442         } catch (e) {
21443             nodeRange.selectNodeContents(node);
21444         }
21445         
21446         
21447         range.collapse(true);
21448     
21449         nodeRange.collapse(true);
21450      
21451         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21452         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
21453          
21454         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21455         
21456         var nodeIsBefore   =  ss == 1;
21457         var nodeIsAfter    = ee == -1;
21458         
21459         if (nodeIsBefore && nodeIsAfter) {
21460             return 0; // outer
21461         }
21462         if (!nodeIsBefore && nodeIsAfter) {
21463             return 1; //right trailed.
21464         }
21465         
21466         if (nodeIsBefore && !nodeIsAfter) {
21467             return 2;  // left trailed.
21468         }
21469         // fully contined.
21470         return 3;
21471     },
21472
21473     // private? - in a new class?
21474     cleanUpPaste :  function()
21475     {
21476         // cleans up the whole document..
21477         Roo.log('cleanuppaste');
21478         
21479         this.cleanUpChildren(this.doc.body);
21480         var clean = this.cleanWordChars(this.doc.body.innerHTML);
21481         if (clean != this.doc.body.innerHTML) {
21482             this.doc.body.innerHTML = clean;
21483         }
21484         
21485     },
21486     
21487     cleanWordChars : function(input) {// change the chars to hex code
21488         var he = Roo.HtmlEditorCore;
21489         
21490         var output = input;
21491         Roo.each(he.swapCodes, function(sw) { 
21492             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21493             
21494             output = output.replace(swapper, sw[1]);
21495         });
21496         
21497         return output;
21498     },
21499     
21500     
21501     cleanUpChildren : function (n)
21502     {
21503         if (!n.childNodes.length) {
21504             return;
21505         }
21506         for (var i = n.childNodes.length-1; i > -1 ; i--) {
21507            this.cleanUpChild(n.childNodes[i]);
21508         }
21509     },
21510     
21511     
21512         
21513     
21514     cleanUpChild : function (node)
21515     {
21516         var ed = this;
21517         //console.log(node);
21518         if (node.nodeName == "#text") {
21519             // clean up silly Windows -- stuff?
21520             return; 
21521         }
21522         if (node.nodeName == "#comment") {
21523             node.parentNode.removeChild(node);
21524             // clean up silly Windows -- stuff?
21525             return; 
21526         }
21527         var lcname = node.tagName.toLowerCase();
21528         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21529         // whitelist of tags..
21530         
21531         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21532             // remove node.
21533             node.parentNode.removeChild(node);
21534             return;
21535             
21536         }
21537         
21538         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21539         
21540         // spans with no attributes - just remove them..
21541         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
21542             remove_keep_children = true;
21543         }
21544         
21545         // remove <a name=....> as rendering on yahoo mailer is borked with this.
21546         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21547         
21548         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21549         //    remove_keep_children = true;
21550         //}
21551         
21552         if (remove_keep_children) {
21553             this.cleanUpChildren(node);
21554             // inserts everything just before this node...
21555             while (node.childNodes.length) {
21556                 var cn = node.childNodes[0];
21557                 node.removeChild(cn);
21558                 node.parentNode.insertBefore(cn, node);
21559             }
21560             node.parentNode.removeChild(node);
21561             return;
21562         }
21563         
21564         if (!node.attributes || !node.attributes.length) {
21565             
21566           
21567             
21568             
21569             this.cleanUpChildren(node);
21570             return;
21571         }
21572         
21573         function cleanAttr(n,v)
21574         {
21575             
21576             if (v.match(/^\./) || v.match(/^\//)) {
21577                 return;
21578             }
21579             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
21580                 return;
21581             }
21582             if (v.match(/^#/)) {
21583                 return;
21584             }
21585             if (v.match(/^\{/)) { // allow template editing.
21586                 return;
21587             }
21588 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21589             node.removeAttribute(n);
21590             
21591         }
21592         
21593         var cwhite = this.cwhite;
21594         var cblack = this.cblack;
21595             
21596         function cleanStyle(n,v)
21597         {
21598             if (v.match(/expression/)) { //XSS?? should we even bother..
21599                 node.removeAttribute(n);
21600                 return;
21601             }
21602             
21603             var parts = v.split(/;/);
21604             var clean = [];
21605             
21606             Roo.each(parts, function(p) {
21607                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21608                 if (!p.length) {
21609                     return true;
21610                 }
21611                 var l = p.split(':').shift().replace(/\s+/g,'');
21612                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21613                 
21614                 if ( cwhite.length && cblack.indexOf(l) > -1) {
21615 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21616                     //node.removeAttribute(n);
21617                     return true;
21618                 }
21619                 //Roo.log()
21620                 // only allow 'c whitelisted system attributes'
21621                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
21622 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21623                     //node.removeAttribute(n);
21624                     return true;
21625                 }
21626                 
21627                 
21628                  
21629                 
21630                 clean.push(p);
21631                 return true;
21632             });
21633             if (clean.length) { 
21634                 node.setAttribute(n, clean.join(';'));
21635             } else {
21636                 node.removeAttribute(n);
21637             }
21638             
21639         }
21640         
21641         
21642         for (var i = node.attributes.length-1; i > -1 ; i--) {
21643             var a = node.attributes[i];
21644             //console.log(a);
21645             
21646             if (a.name.toLowerCase().substr(0,2)=='on')  {
21647                 node.removeAttribute(a.name);
21648                 continue;
21649             }
21650             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21651                 node.removeAttribute(a.name);
21652                 continue;
21653             }
21654             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21655                 cleanAttr(a.name,a.value); // fixme..
21656                 continue;
21657             }
21658             if (a.name == 'style') {
21659                 cleanStyle(a.name,a.value);
21660                 continue;
21661             }
21662             /// clean up MS crap..
21663             // tecnically this should be a list of valid class'es..
21664             
21665             
21666             if (a.name == 'class') {
21667                 if (a.value.match(/^Mso/)) {
21668                     node.removeAttribute('class');
21669                 }
21670                 
21671                 if (a.value.match(/^body$/)) {
21672                     node.removeAttribute('class');
21673                 }
21674                 continue;
21675             }
21676             
21677             // style cleanup!?
21678             // class cleanup?
21679             
21680         }
21681         
21682         
21683         this.cleanUpChildren(node);
21684         
21685         
21686     },
21687     
21688     /**
21689      * Clean up MS wordisms...
21690      */
21691     cleanWord : function(node)
21692     {
21693         if (!node) {
21694             this.cleanWord(this.doc.body);
21695             return;
21696         }
21697         
21698         if(
21699                 node.nodeName == 'SPAN' &&
21700                 !node.hasAttributes() &&
21701                 node.childNodes.length == 1 &&
21702                 node.firstChild.nodeName == "#text"  
21703         ) {
21704             var textNode = node.firstChild;
21705             node.removeChild(textNode);
21706             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21707                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
21708             }
21709             node.parentNode.insertBefore(textNode, node);
21710             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
21711                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
21712             }
21713             node.parentNode.removeChild(node);
21714         }
21715         
21716         if (node.nodeName == "#text") {
21717             // clean up silly Windows -- stuff?
21718             return; 
21719         }
21720         if (node.nodeName == "#comment") {
21721             node.parentNode.removeChild(node);
21722             // clean up silly Windows -- stuff?
21723             return; 
21724         }
21725         
21726         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21727             node.parentNode.removeChild(node);
21728             return;
21729         }
21730         //Roo.log(node.tagName);
21731         // remove - but keep children..
21732         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
21733             //Roo.log('-- removed');
21734             while (node.childNodes.length) {
21735                 var cn = node.childNodes[0];
21736                 node.removeChild(cn);
21737                 node.parentNode.insertBefore(cn, node);
21738                 // move node to parent - and clean it..
21739                 this.cleanWord(cn);
21740             }
21741             node.parentNode.removeChild(node);
21742             /// no need to iterate chidlren = it's got none..
21743             //this.iterateChildren(node, this.cleanWord);
21744             return;
21745         }
21746         // clean styles
21747         if (node.className.length) {
21748             
21749             var cn = node.className.split(/\W+/);
21750             var cna = [];
21751             Roo.each(cn, function(cls) {
21752                 if (cls.match(/Mso[a-zA-Z]+/)) {
21753                     return;
21754                 }
21755                 cna.push(cls);
21756             });
21757             node.className = cna.length ? cna.join(' ') : '';
21758             if (!cna.length) {
21759                 node.removeAttribute("class");
21760             }
21761         }
21762         
21763         if (node.hasAttribute("lang")) {
21764             node.removeAttribute("lang");
21765         }
21766         
21767         if (node.hasAttribute("style")) {
21768             
21769             var styles = node.getAttribute("style").split(";");
21770             var nstyle = [];
21771             Roo.each(styles, function(s) {
21772                 if (!s.match(/:/)) {
21773                     return;
21774                 }
21775                 var kv = s.split(":");
21776                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21777                     return;
21778                 }
21779                 // what ever is left... we allow.
21780                 nstyle.push(s);
21781             });
21782             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21783             if (!nstyle.length) {
21784                 node.removeAttribute('style');
21785             }
21786         }
21787         this.iterateChildren(node, this.cleanWord);
21788         
21789         
21790         
21791     },
21792     /**
21793      * iterateChildren of a Node, calling fn each time, using this as the scole..
21794      * @param {DomNode} node node to iterate children of.
21795      * @param {Function} fn method of this class to call on each item.
21796      */
21797     iterateChildren : function(node, fn)
21798     {
21799         if (!node.childNodes.length) {
21800                 return;
21801         }
21802         for (var i = node.childNodes.length-1; i > -1 ; i--) {
21803            fn.call(this, node.childNodes[i])
21804         }
21805     },
21806     
21807     
21808     /**
21809      * cleanTableWidths.
21810      *
21811      * Quite often pasting from word etc.. results in tables with column and widths.
21812      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21813      *
21814      */
21815     cleanTableWidths : function(node)
21816     {
21817          
21818          
21819         if (!node) {
21820             this.cleanTableWidths(this.doc.body);
21821             return;
21822         }
21823         
21824         // ignore list...
21825         if (node.nodeName == "#text" || node.nodeName == "#comment") {
21826             return; 
21827         }
21828         Roo.log(node.tagName);
21829         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21830             this.iterateChildren(node, this.cleanTableWidths);
21831             return;
21832         }
21833         if (node.hasAttribute('width')) {
21834             node.removeAttribute('width');
21835         }
21836         
21837          
21838         if (node.hasAttribute("style")) {
21839             // pretty basic...
21840             
21841             var styles = node.getAttribute("style").split(";");
21842             var nstyle = [];
21843             Roo.each(styles, function(s) {
21844                 if (!s.match(/:/)) {
21845                     return;
21846                 }
21847                 var kv = s.split(":");
21848                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21849                     return;
21850                 }
21851                 // what ever is left... we allow.
21852                 nstyle.push(s);
21853             });
21854             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21855             if (!nstyle.length) {
21856                 node.removeAttribute('style');
21857             }
21858         }
21859         
21860         this.iterateChildren(node, this.cleanTableWidths);
21861         
21862         
21863     },
21864     
21865     
21866     
21867     
21868     domToHTML : function(currentElement, depth, nopadtext) {
21869         
21870         depth = depth || 0;
21871         nopadtext = nopadtext || false;
21872     
21873         if (!currentElement) {
21874             return this.domToHTML(this.doc.body);
21875         }
21876         
21877         //Roo.log(currentElement);
21878         var j;
21879         var allText = false;
21880         var nodeName = currentElement.nodeName;
21881         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21882         
21883         if  (nodeName == '#text') {
21884             
21885             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21886         }
21887         
21888         
21889         var ret = '';
21890         if (nodeName != 'BODY') {
21891              
21892             var i = 0;
21893             // Prints the node tagName, such as <A>, <IMG>, etc
21894             if (tagName) {
21895                 var attr = [];
21896                 for(i = 0; i < currentElement.attributes.length;i++) {
21897                     // quoting?
21898                     var aname = currentElement.attributes.item(i).name;
21899                     if (!currentElement.attributes.item(i).value.length) {
21900                         continue;
21901                     }
21902                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21903                 }
21904                 
21905                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21906             } 
21907             else {
21908                 
21909                 // eack
21910             }
21911         } else {
21912             tagName = false;
21913         }
21914         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21915             return ret;
21916         }
21917         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21918             nopadtext = true;
21919         }
21920         
21921         
21922         // Traverse the tree
21923         i = 0;
21924         var currentElementChild = currentElement.childNodes.item(i);
21925         var allText = true;
21926         var innerHTML  = '';
21927         lastnode = '';
21928         while (currentElementChild) {
21929             // Formatting code (indent the tree so it looks nice on the screen)
21930             var nopad = nopadtext;
21931             if (lastnode == 'SPAN') {
21932                 nopad  = true;
21933             }
21934             // text
21935             if  (currentElementChild.nodeName == '#text') {
21936                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21937                 toadd = nopadtext ? toadd : toadd.trim();
21938                 if (!nopad && toadd.length > 80) {
21939                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21940                 }
21941                 innerHTML  += toadd;
21942                 
21943                 i++;
21944                 currentElementChild = currentElement.childNodes.item(i);
21945                 lastNode = '';
21946                 continue;
21947             }
21948             allText = false;
21949             
21950             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21951                 
21952             // Recursively traverse the tree structure of the child node
21953             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21954             lastnode = currentElementChild.nodeName;
21955             i++;
21956             currentElementChild=currentElement.childNodes.item(i);
21957         }
21958         
21959         ret += innerHTML;
21960         
21961         if (!allText) {
21962                 // The remaining code is mostly for formatting the tree
21963             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21964         }
21965         
21966         
21967         if (tagName) {
21968             ret+= "</"+tagName+">";
21969         }
21970         return ret;
21971         
21972     },
21973         
21974     applyBlacklists : function()
21975     {
21976         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21977         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21978         
21979         this.white = [];
21980         this.black = [];
21981         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21982             if (b.indexOf(tag) > -1) {
21983                 return;
21984             }
21985             this.white.push(tag);
21986             
21987         }, this);
21988         
21989         Roo.each(w, function(tag) {
21990             if (b.indexOf(tag) > -1) {
21991                 return;
21992             }
21993             if (this.white.indexOf(tag) > -1) {
21994                 return;
21995             }
21996             this.white.push(tag);
21997             
21998         }, this);
21999         
22000         
22001         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22002             if (w.indexOf(tag) > -1) {
22003                 return;
22004             }
22005             this.black.push(tag);
22006             
22007         }, this);
22008         
22009         Roo.each(b, function(tag) {
22010             if (w.indexOf(tag) > -1) {
22011                 return;
22012             }
22013             if (this.black.indexOf(tag) > -1) {
22014                 return;
22015             }
22016             this.black.push(tag);
22017             
22018         }, this);
22019         
22020         
22021         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22022         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22023         
22024         this.cwhite = [];
22025         this.cblack = [];
22026         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22027             if (b.indexOf(tag) > -1) {
22028                 return;
22029             }
22030             this.cwhite.push(tag);
22031             
22032         }, this);
22033         
22034         Roo.each(w, function(tag) {
22035             if (b.indexOf(tag) > -1) {
22036                 return;
22037             }
22038             if (this.cwhite.indexOf(tag) > -1) {
22039                 return;
22040             }
22041             this.cwhite.push(tag);
22042             
22043         }, this);
22044         
22045         
22046         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22047             if (w.indexOf(tag) > -1) {
22048                 return;
22049             }
22050             this.cblack.push(tag);
22051             
22052         }, this);
22053         
22054         Roo.each(b, function(tag) {
22055             if (w.indexOf(tag) > -1) {
22056                 return;
22057             }
22058             if (this.cblack.indexOf(tag) > -1) {
22059                 return;
22060             }
22061             this.cblack.push(tag);
22062             
22063         }, this);
22064     },
22065     
22066     setStylesheets : function(stylesheets)
22067     {
22068         if(typeof(stylesheets) == 'string'){
22069             Roo.get(this.iframe.contentDocument.head).createChild({
22070                 tag : 'link',
22071                 rel : 'stylesheet',
22072                 type : 'text/css',
22073                 href : stylesheets
22074             });
22075             
22076             return;
22077         }
22078         var _this = this;
22079      
22080         Roo.each(stylesheets, function(s) {
22081             if(!s.length){
22082                 return;
22083             }
22084             
22085             Roo.get(_this.iframe.contentDocument.head).createChild({
22086                 tag : 'link',
22087                 rel : 'stylesheet',
22088                 type : 'text/css',
22089                 href : s
22090             });
22091         });
22092
22093         
22094     },
22095     
22096     removeStylesheets : function()
22097     {
22098         var _this = this;
22099         
22100         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22101             s.remove();
22102         });
22103     },
22104     
22105     setStyle : function(style)
22106     {
22107         Roo.get(this.iframe.contentDocument.head).createChild({
22108             tag : 'style',
22109             type : 'text/css',
22110             html : style
22111         });
22112
22113         return;
22114     }
22115     
22116     // hide stuff that is not compatible
22117     /**
22118      * @event blur
22119      * @hide
22120      */
22121     /**
22122      * @event change
22123      * @hide
22124      */
22125     /**
22126      * @event focus
22127      * @hide
22128      */
22129     /**
22130      * @event specialkey
22131      * @hide
22132      */
22133     /**
22134      * @cfg {String} fieldClass @hide
22135      */
22136     /**
22137      * @cfg {String} focusClass @hide
22138      */
22139     /**
22140      * @cfg {String} autoCreate @hide
22141      */
22142     /**
22143      * @cfg {String} inputType @hide
22144      */
22145     /**
22146      * @cfg {String} invalidClass @hide
22147      */
22148     /**
22149      * @cfg {String} invalidText @hide
22150      */
22151     /**
22152      * @cfg {String} msgFx @hide
22153      */
22154     /**
22155      * @cfg {String} validateOnBlur @hide
22156      */
22157 });
22158
22159 Roo.HtmlEditorCore.white = [
22160         'area', 'br', 'img', 'input', 'hr', 'wbr',
22161         
22162        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22163        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22164        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22165        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22166        'table',   'ul',         'xmp', 
22167        
22168        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22169       'thead',   'tr', 
22170      
22171       'dir', 'menu', 'ol', 'ul', 'dl',
22172        
22173       'embed',  'object'
22174 ];
22175
22176
22177 Roo.HtmlEditorCore.black = [
22178     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22179         'applet', // 
22180         'base',   'basefont', 'bgsound', 'blink',  'body', 
22181         'frame',  'frameset', 'head',    'html',   'ilayer', 
22182         'iframe', 'layer',  'link',     'meta',    'object',   
22183         'script', 'style' ,'title',  'xml' // clean later..
22184 ];
22185 Roo.HtmlEditorCore.clean = [
22186     'script', 'style', 'title', 'xml'
22187 ];
22188 Roo.HtmlEditorCore.remove = [
22189     'font'
22190 ];
22191 // attributes..
22192
22193 Roo.HtmlEditorCore.ablack = [
22194     'on'
22195 ];
22196     
22197 Roo.HtmlEditorCore.aclean = [ 
22198     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22199 ];
22200
22201 // protocols..
22202 Roo.HtmlEditorCore.pwhite= [
22203         'http',  'https',  'mailto'
22204 ];
22205
22206 // white listed style attributes.
22207 Roo.HtmlEditorCore.cwhite= [
22208       //  'text-align', /// default is to allow most things..
22209       
22210          
22211 //        'font-size'//??
22212 ];
22213
22214 // black listed style attributes.
22215 Roo.HtmlEditorCore.cblack= [
22216       //  'font-size' -- this can be set by the project 
22217 ];
22218
22219
22220 Roo.HtmlEditorCore.swapCodes   =[ 
22221     [    8211, "--" ], 
22222     [    8212, "--" ], 
22223     [    8216,  "'" ],  
22224     [    8217, "'" ],  
22225     [    8220, '"' ],  
22226     [    8221, '"' ],  
22227     [    8226, "*" ],  
22228     [    8230, "..." ]
22229 ]; 
22230
22231     //<script type="text/javascript">
22232
22233 /*
22234  * Ext JS Library 1.1.1
22235  * Copyright(c) 2006-2007, Ext JS, LLC.
22236  * Licence LGPL
22237  * 
22238  */
22239  
22240  
22241 Roo.form.HtmlEditor = function(config){
22242     
22243     
22244     
22245     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
22246     
22247     if (!this.toolbars) {
22248         this.toolbars = [];
22249     }
22250     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22251     
22252     
22253 };
22254
22255 /**
22256  * @class Roo.form.HtmlEditor
22257  * @extends Roo.form.Field
22258  * Provides a lightweight HTML Editor component.
22259  *
22260  * This has been tested on Fireforx / Chrome.. IE may not be so great..
22261  * 
22262  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
22263  * supported by this editor.</b><br/><br/>
22264  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
22265  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
22266  */
22267 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
22268     /**
22269      * @cfg {Boolean} clearUp
22270      */
22271     clearUp : true,
22272       /**
22273      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22274      */
22275     toolbars : false,
22276    
22277      /**
22278      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22279      *                        Roo.resizable.
22280      */
22281     resizable : false,
22282      /**
22283      * @cfg {Number} height (in pixels)
22284      */   
22285     height: 300,
22286    /**
22287      * @cfg {Number} width (in pixels)
22288      */   
22289     width: 500,
22290     
22291     /**
22292      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22293      * 
22294      */
22295     stylesheets: false,
22296     
22297     
22298      /**
22299      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
22300      * 
22301      */
22302     cblack: false,
22303     /**
22304      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
22305      * 
22306      */
22307     cwhite: false,
22308     
22309      /**
22310      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
22311      * 
22312      */
22313     black: false,
22314     /**
22315      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
22316      * 
22317      */
22318     white: false,
22319     
22320     // id of frame..
22321     frameId: false,
22322     
22323     // private properties
22324     validationEvent : false,
22325     deferHeight: true,
22326     initialized : false,
22327     activated : false,
22328     
22329     onFocus : Roo.emptyFn,
22330     iframePad:3,
22331     hideMode:'offsets',
22332     
22333     actionMode : 'container', // defaults to hiding it...
22334     
22335     defaultAutoCreate : { // modified by initCompnoent..
22336         tag: "textarea",
22337         style:"width:500px;height:300px;",
22338         autocomplete: "new-password"
22339     },
22340
22341     // private
22342     initComponent : function(){
22343         this.addEvents({
22344             /**
22345              * @event initialize
22346              * Fires when the editor is fully initialized (including the iframe)
22347              * @param {HtmlEditor} this
22348              */
22349             initialize: true,
22350             /**
22351              * @event activate
22352              * Fires when the editor is first receives the focus. Any insertion must wait
22353              * until after this event.
22354              * @param {HtmlEditor} this
22355              */
22356             activate: true,
22357              /**
22358              * @event beforesync
22359              * Fires before the textarea is updated with content from the editor iframe. Return false
22360              * to cancel the sync.
22361              * @param {HtmlEditor} this
22362              * @param {String} html
22363              */
22364             beforesync: true,
22365              /**
22366              * @event beforepush
22367              * Fires before the iframe editor is updated with content from the textarea. Return false
22368              * to cancel the push.
22369              * @param {HtmlEditor} this
22370              * @param {String} html
22371              */
22372             beforepush: true,
22373              /**
22374              * @event sync
22375              * Fires when the textarea is updated with content from the editor iframe.
22376              * @param {HtmlEditor} this
22377              * @param {String} html
22378              */
22379             sync: true,
22380              /**
22381              * @event push
22382              * Fires when the iframe editor is updated with content from the textarea.
22383              * @param {HtmlEditor} this
22384              * @param {String} html
22385              */
22386             push: true,
22387              /**
22388              * @event editmodechange
22389              * Fires when the editor switches edit modes
22390              * @param {HtmlEditor} this
22391              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22392              */
22393             editmodechange: true,
22394             /**
22395              * @event editorevent
22396              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22397              * @param {HtmlEditor} this
22398              */
22399             editorevent: true,
22400             /**
22401              * @event firstfocus
22402              * Fires when on first focus - needed by toolbars..
22403              * @param {HtmlEditor} this
22404              */
22405             firstfocus: true,
22406             /**
22407              * @event autosave
22408              * Auto save the htmlEditor value as a file into Events
22409              * @param {HtmlEditor} this
22410              */
22411             autosave: true,
22412             /**
22413              * @event savedpreview
22414              * preview the saved version of htmlEditor
22415              * @param {HtmlEditor} this
22416              */
22417             savedpreview: true,
22418             
22419             /**
22420             * @event stylesheetsclick
22421             * Fires when press the Sytlesheets button
22422             * @param {Roo.HtmlEditorCore} this
22423             */
22424             stylesheetsclick: true
22425         });
22426         this.defaultAutoCreate =  {
22427             tag: "textarea",
22428             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
22429             autocomplete: "new-password"
22430         };
22431     },
22432
22433     /**
22434      * Protected method that will not generally be called directly. It
22435      * is called when the editor creates its toolbar. Override this method if you need to
22436      * add custom toolbar buttons.
22437      * @param {HtmlEditor} editor
22438      */
22439     createToolbar : function(editor){
22440         Roo.log("create toolbars");
22441         if (!editor.toolbars || !editor.toolbars.length) {
22442             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
22443         }
22444         
22445         for (var i =0 ; i < editor.toolbars.length;i++) {
22446             editor.toolbars[i] = Roo.factory(
22447                     typeof(editor.toolbars[i]) == 'string' ?
22448                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
22449                 Roo.form.HtmlEditor);
22450             editor.toolbars[i].init(editor);
22451         }
22452          
22453         
22454     },
22455
22456      
22457     // private
22458     onRender : function(ct, position)
22459     {
22460         var _t = this;
22461         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
22462         
22463         this.wrap = this.el.wrap({
22464             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22465         });
22466         
22467         this.editorcore.onRender(ct, position);
22468          
22469         if (this.resizable) {
22470             this.resizeEl = new Roo.Resizable(this.wrap, {
22471                 pinned : true,
22472                 wrap: true,
22473                 dynamic : true,
22474                 minHeight : this.height,
22475                 height: this.height,
22476                 handles : this.resizable,
22477                 width: this.width,
22478                 listeners : {
22479                     resize : function(r, w, h) {
22480                         _t.onResize(w,h); // -something
22481                     }
22482                 }
22483             });
22484             
22485         }
22486         this.createToolbar(this);
22487        
22488         
22489         if(!this.width){
22490             this.setSize(this.wrap.getSize());
22491         }
22492         if (this.resizeEl) {
22493             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22494             // should trigger onReize..
22495         }
22496         
22497         this.keyNav = new Roo.KeyNav(this.el, {
22498             
22499             "tab" : function(e){
22500                 e.preventDefault();
22501                 
22502                 var value = this.getValue();
22503                 
22504                 var start = this.el.dom.selectionStart;
22505                 var end = this.el.dom.selectionEnd;
22506                 
22507                 if(!e.shiftKey){
22508                     
22509                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
22510                     this.el.dom.setSelectionRange(end + 1, end + 1);
22511                     return;
22512                 }
22513                 
22514                 var f = value.substring(0, start).split("\t");
22515                 
22516                 if(f.pop().length != 0){
22517                     return;
22518                 }
22519                 
22520                 this.setValue(f.join("\t") + value.substring(end));
22521                 this.el.dom.setSelectionRange(start - 1, start - 1);
22522                 
22523             },
22524             
22525             "home" : function(e){
22526                 e.preventDefault();
22527                 
22528                 var curr = this.el.dom.selectionStart;
22529                 var lines = this.getValue().split("\n");
22530                 
22531                 if(!lines.length){
22532                     return;
22533                 }
22534                 
22535                 if(e.ctrlKey){
22536                     this.el.dom.setSelectionRange(0, 0);
22537                     return;
22538                 }
22539                 
22540                 var pos = 0;
22541                 
22542                 for (var i = 0; i < lines.length;i++) {
22543                     pos += lines[i].length;
22544                     
22545                     if(i != 0){
22546                         pos += 1;
22547                     }
22548                     
22549                     if(pos < curr){
22550                         continue;
22551                     }
22552                     
22553                     pos -= lines[i].length;
22554                     
22555                     break;
22556                 }
22557                 
22558                 if(!e.shiftKey){
22559                     this.el.dom.setSelectionRange(pos, pos);
22560                     return;
22561                 }
22562                 
22563                 this.el.dom.selectionStart = pos;
22564                 this.el.dom.selectionEnd = curr;
22565             },
22566             
22567             "end" : function(e){
22568                 e.preventDefault();
22569                 
22570                 var curr = this.el.dom.selectionStart;
22571                 var lines = this.getValue().split("\n");
22572                 
22573                 if(!lines.length){
22574                     return;
22575                 }
22576                 
22577                 if(e.ctrlKey){
22578                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
22579                     return;
22580                 }
22581                 
22582                 var pos = 0;
22583                 
22584                 for (var i = 0; i < lines.length;i++) {
22585                     
22586                     pos += lines[i].length;
22587                     
22588                     if(i != 0){
22589                         pos += 1;
22590                     }
22591                     
22592                     if(pos < curr){
22593                         continue;
22594                     }
22595                     
22596                     break;
22597                 }
22598                 
22599                 if(!e.shiftKey){
22600                     this.el.dom.setSelectionRange(pos, pos);
22601                     return;
22602                 }
22603                 
22604                 this.el.dom.selectionStart = curr;
22605                 this.el.dom.selectionEnd = pos;
22606             },
22607
22608             scope : this,
22609
22610             doRelay : function(foo, bar, hname){
22611                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
22612             },
22613
22614             forceKeyDown: true
22615         });
22616         
22617 //        if(this.autosave && this.w){
22618 //            this.autoSaveFn = setInterval(this.autosave, 1000);
22619 //        }
22620     },
22621
22622     // private
22623     onResize : function(w, h)
22624     {
22625         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
22626         var ew = false;
22627         var eh = false;
22628         
22629         if(this.el ){
22630             if(typeof w == 'number'){
22631                 var aw = w - this.wrap.getFrameWidth('lr');
22632                 this.el.setWidth(this.adjustWidth('textarea', aw));
22633                 ew = aw;
22634             }
22635             if(typeof h == 'number'){
22636                 var tbh = 0;
22637                 for (var i =0; i < this.toolbars.length;i++) {
22638                     // fixme - ask toolbars for heights?
22639                     tbh += this.toolbars[i].tb.el.getHeight();
22640                     if (this.toolbars[i].footer) {
22641                         tbh += this.toolbars[i].footer.el.getHeight();
22642                     }
22643                 }
22644                 
22645                 
22646                 
22647                 
22648                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22649                 ah -= 5; // knock a few pixes off for look..
22650 //                Roo.log(ah);
22651                 this.el.setHeight(this.adjustWidth('textarea', ah));
22652                 var eh = ah;
22653             }
22654         }
22655         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22656         this.editorcore.onResize(ew,eh);
22657         
22658     },
22659
22660     /**
22661      * Toggles the editor between standard and source edit mode.
22662      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22663      */
22664     toggleSourceEdit : function(sourceEditMode)
22665     {
22666         this.editorcore.toggleSourceEdit(sourceEditMode);
22667         
22668         if(this.editorcore.sourceEditMode){
22669             Roo.log('editor - showing textarea');
22670             
22671 //            Roo.log('in');
22672 //            Roo.log(this.syncValue());
22673             this.editorcore.syncValue();
22674             this.el.removeClass('x-hidden');
22675             this.el.dom.removeAttribute('tabIndex');
22676             this.el.focus();
22677             
22678             for (var i = 0; i < this.toolbars.length; i++) {
22679                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22680                     this.toolbars[i].tb.hide();
22681                     this.toolbars[i].footer.hide();
22682                 }
22683             }
22684             
22685         }else{
22686             Roo.log('editor - hiding textarea');
22687 //            Roo.log('out')
22688 //            Roo.log(this.pushValue()); 
22689             this.editorcore.pushValue();
22690             
22691             this.el.addClass('x-hidden');
22692             this.el.dom.setAttribute('tabIndex', -1);
22693             
22694             for (var i = 0; i < this.toolbars.length; i++) {
22695                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
22696                     this.toolbars[i].tb.show();
22697                     this.toolbars[i].footer.show();
22698                 }
22699             }
22700             
22701             //this.deferFocus();
22702         }
22703         
22704         this.setSize(this.wrap.getSize());
22705         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
22706         
22707         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22708     },
22709  
22710     // private (for BoxComponent)
22711     adjustSize : Roo.BoxComponent.prototype.adjustSize,
22712
22713     // private (for BoxComponent)
22714     getResizeEl : function(){
22715         return this.wrap;
22716     },
22717
22718     // private (for BoxComponent)
22719     getPositionEl : function(){
22720         return this.wrap;
22721     },
22722
22723     // private
22724     initEvents : function(){
22725         this.originalValue = this.getValue();
22726     },
22727
22728     /**
22729      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22730      * @method
22731      */
22732     markInvalid : Roo.emptyFn,
22733     /**
22734      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22735      * @method
22736      */
22737     clearInvalid : Roo.emptyFn,
22738
22739     setValue : function(v){
22740         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
22741         this.editorcore.pushValue();
22742     },
22743
22744      
22745     // private
22746     deferFocus : function(){
22747         this.focus.defer(10, this);
22748     },
22749
22750     // doc'ed in Field
22751     focus : function(){
22752         this.editorcore.focus();
22753         
22754     },
22755       
22756
22757     // private
22758     onDestroy : function(){
22759         
22760         
22761         
22762         if(this.rendered){
22763             
22764             for (var i =0; i < this.toolbars.length;i++) {
22765                 // fixme - ask toolbars for heights?
22766                 this.toolbars[i].onDestroy();
22767             }
22768             
22769             this.wrap.dom.innerHTML = '';
22770             this.wrap.remove();
22771         }
22772     },
22773
22774     // private
22775     onFirstFocus : function(){
22776         //Roo.log("onFirstFocus");
22777         this.editorcore.onFirstFocus();
22778          for (var i =0; i < this.toolbars.length;i++) {
22779             this.toolbars[i].onFirstFocus();
22780         }
22781         
22782     },
22783     
22784     // private
22785     syncValue : function()
22786     {
22787         this.editorcore.syncValue();
22788     },
22789     
22790     pushValue : function()
22791     {
22792         this.editorcore.pushValue();
22793     },
22794     
22795     setStylesheets : function(stylesheets)
22796     {
22797         this.editorcore.setStylesheets(stylesheets);
22798     },
22799     
22800     removeStylesheets : function()
22801     {
22802         this.editorcore.removeStylesheets();
22803     }
22804      
22805     
22806     // hide stuff that is not compatible
22807     /**
22808      * @event blur
22809      * @hide
22810      */
22811     /**
22812      * @event change
22813      * @hide
22814      */
22815     /**
22816      * @event focus
22817      * @hide
22818      */
22819     /**
22820      * @event specialkey
22821      * @hide
22822      */
22823     /**
22824      * @cfg {String} fieldClass @hide
22825      */
22826     /**
22827      * @cfg {String} focusClass @hide
22828      */
22829     /**
22830      * @cfg {String} autoCreate @hide
22831      */
22832     /**
22833      * @cfg {String} inputType @hide
22834      */
22835     /**
22836      * @cfg {String} invalidClass @hide
22837      */
22838     /**
22839      * @cfg {String} invalidText @hide
22840      */
22841     /**
22842      * @cfg {String} msgFx @hide
22843      */
22844     /**
22845      * @cfg {String} validateOnBlur @hide
22846      */
22847 });
22848  
22849     // <script type="text/javascript">
22850 /*
22851  * Based on
22852  * Ext JS Library 1.1.1
22853  * Copyright(c) 2006-2007, Ext JS, LLC.
22854  *  
22855  
22856  */
22857
22858 /**
22859  * @class Roo.form.HtmlEditorToolbar1
22860  * Basic Toolbar
22861  * 
22862  * Usage:
22863  *
22864  new Roo.form.HtmlEditor({
22865     ....
22866     toolbars : [
22867         new Roo.form.HtmlEditorToolbar1({
22868             disable : { fonts: 1 , format: 1, ..., ... , ...],
22869             btns : [ .... ]
22870         })
22871     }
22872      
22873  * 
22874  * @cfg {Object} disable List of elements to disable..
22875  * @cfg {Array} btns List of additional buttons.
22876  * 
22877  * 
22878  * NEEDS Extra CSS? 
22879  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22880  */
22881  
22882 Roo.form.HtmlEditor.ToolbarStandard = function(config)
22883 {
22884     
22885     Roo.apply(this, config);
22886     
22887     // default disabled, based on 'good practice'..
22888     this.disable = this.disable || {};
22889     Roo.applyIf(this.disable, {
22890         fontSize : true,
22891         colors : true,
22892         specialElements : true
22893     });
22894     
22895     
22896     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22897     // dont call parent... till later.
22898 }
22899
22900 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
22901     
22902     tb: false,
22903     
22904     rendered: false,
22905     
22906     editor : false,
22907     editorcore : false,
22908     /**
22909      * @cfg {Object} disable  List of toolbar elements to disable
22910          
22911      */
22912     disable : false,
22913     
22914     
22915      /**
22916      * @cfg {String} createLinkText The default text for the create link prompt
22917      */
22918     createLinkText : 'Please enter the URL for the link:',
22919     /**
22920      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
22921      */
22922     defaultLinkValue : 'http:/'+'/',
22923    
22924     
22925       /**
22926      * @cfg {Array} fontFamilies An array of available font families
22927      */
22928     fontFamilies : [
22929         'Arial',
22930         'Courier New',
22931         'Tahoma',
22932         'Times New Roman',
22933         'Verdana'
22934     ],
22935     
22936     specialChars : [
22937            "&#169;",
22938           "&#174;",     
22939           "&#8482;",    
22940           "&#163;" ,    
22941          // "&#8212;",    
22942           "&#8230;",    
22943           "&#247;" ,    
22944         //  "&#225;" ,     ?? a acute?
22945            "&#8364;"    , //Euro
22946        //   "&#8220;"    ,
22947         //  "&#8221;"    ,
22948         //  "&#8226;"    ,
22949           "&#176;"  //   , // degrees
22950
22951          // "&#233;"     , // e ecute
22952          // "&#250;"     , // u ecute?
22953     ],
22954     
22955     specialElements : [
22956         {
22957             text: "Insert Table",
22958             xtype: 'MenuItem',
22959             xns : Roo.Menu,
22960             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
22961                 
22962         },
22963         {    
22964             text: "Insert Image",
22965             xtype: 'MenuItem',
22966             xns : Roo.Menu,
22967             ihtml : '<img src="about:blank"/>'
22968             
22969         }
22970         
22971          
22972     ],
22973     
22974     
22975     inputElements : [ 
22976             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
22977             "input:submit", "input:button", "select", "textarea", "label" ],
22978     formats : [
22979         ["p"] ,  
22980         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
22981         ["pre"],[ "code"], 
22982         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
22983         ['div'],['span'],
22984         ['sup'],['sub']
22985     ],
22986     
22987     cleanStyles : [
22988         "font-size"
22989     ],
22990      /**
22991      * @cfg {String} defaultFont default font to use.
22992      */
22993     defaultFont: 'tahoma',
22994    
22995     fontSelect : false,
22996     
22997     
22998     formatCombo : false,
22999     
23000     init : function(editor)
23001     {
23002         this.editor = editor;
23003         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23004         var editorcore = this.editorcore;
23005         
23006         var _t = this;
23007         
23008         var fid = editorcore.frameId;
23009         var etb = this;
23010         function btn(id, toggle, handler){
23011             var xid = fid + '-'+ id ;
23012             return {
23013                 id : xid,
23014                 cmd : id,
23015                 cls : 'x-btn-icon x-edit-'+id,
23016                 enableToggle:toggle !== false,
23017                 scope: _t, // was editor...
23018                 handler:handler||_t.relayBtnCmd,
23019                 clickEvent:'mousedown',
23020                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23021                 tabIndex:-1
23022             };
23023         }
23024         
23025         
23026         
23027         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
23028         this.tb = tb;
23029          // stop form submits
23030         tb.el.on('click', function(e){
23031             e.preventDefault(); // what does this do?
23032         });
23033
23034         if(!this.disable.font) { // && !Roo.isSafari){
23035             /* why no safari for fonts 
23036             editor.fontSelect = tb.el.createChild({
23037                 tag:'select',
23038                 tabIndex: -1,
23039                 cls:'x-font-select',
23040                 html: this.createFontOptions()
23041             });
23042             
23043             editor.fontSelect.on('change', function(){
23044                 var font = editor.fontSelect.dom.value;
23045                 editor.relayCmd('fontname', font);
23046                 editor.deferFocus();
23047             }, editor);
23048             
23049             tb.add(
23050                 editor.fontSelect.dom,
23051                 '-'
23052             );
23053             */
23054             
23055         };
23056         if(!this.disable.formats){
23057             this.formatCombo = new Roo.form.ComboBox({
23058                 store: new Roo.data.SimpleStore({
23059                     id : 'tag',
23060                     fields: ['tag'],
23061                     data : this.formats // from states.js
23062                 }),
23063                 blockFocus : true,
23064                 name : '',
23065                 //autoCreate : {tag: "div",  size: "20"},
23066                 displayField:'tag',
23067                 typeAhead: false,
23068                 mode: 'local',
23069                 editable : false,
23070                 triggerAction: 'all',
23071                 emptyText:'Add tag',
23072                 selectOnFocus:true,
23073                 width:135,
23074                 listeners : {
23075                     'select': function(c, r, i) {
23076                         editorcore.insertTag(r.get('tag'));
23077                         editor.focus();
23078                     }
23079                 }
23080
23081             });
23082             tb.addField(this.formatCombo);
23083             
23084         }
23085         
23086         if(!this.disable.format){
23087             tb.add(
23088                 btn('bold'),
23089                 btn('italic'),
23090                 btn('underline'),
23091                 btn('strikethrough')
23092             );
23093         };
23094         if(!this.disable.fontSize){
23095             tb.add(
23096                 '-',
23097                 
23098                 
23099                 btn('increasefontsize', false, editorcore.adjustFont),
23100                 btn('decreasefontsize', false, editorcore.adjustFont)
23101             );
23102         };
23103         
23104         
23105         if(!this.disable.colors){
23106             tb.add(
23107                 '-', {
23108                     id:editorcore.frameId +'-forecolor',
23109                     cls:'x-btn-icon x-edit-forecolor',
23110                     clickEvent:'mousedown',
23111                     tooltip: this.buttonTips['forecolor'] || undefined,
23112                     tabIndex:-1,
23113                     menu : new Roo.menu.ColorMenu({
23114                         allowReselect: true,
23115                         focus: Roo.emptyFn,
23116                         value:'000000',
23117                         plain:true,
23118                         selectHandler: function(cp, color){
23119                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
23120                             editor.deferFocus();
23121                         },
23122                         scope: editorcore,
23123                         clickEvent:'mousedown'
23124                     })
23125                 }, {
23126                     id:editorcore.frameId +'backcolor',
23127                     cls:'x-btn-icon x-edit-backcolor',
23128                     clickEvent:'mousedown',
23129                     tooltip: this.buttonTips['backcolor'] || undefined,
23130                     tabIndex:-1,
23131                     menu : new Roo.menu.ColorMenu({
23132                         focus: Roo.emptyFn,
23133                         value:'FFFFFF',
23134                         plain:true,
23135                         allowReselect: true,
23136                         selectHandler: function(cp, color){
23137                             if(Roo.isGecko){
23138                                 editorcore.execCmd('useCSS', false);
23139                                 editorcore.execCmd('hilitecolor', color);
23140                                 editorcore.execCmd('useCSS', true);
23141                                 editor.deferFocus();
23142                             }else{
23143                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
23144                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
23145                                 editor.deferFocus();
23146                             }
23147                         },
23148                         scope:editorcore,
23149                         clickEvent:'mousedown'
23150                     })
23151                 }
23152             );
23153         };
23154         // now add all the items...
23155         
23156
23157         if(!this.disable.alignments){
23158             tb.add(
23159                 '-',
23160                 btn('justifyleft'),
23161                 btn('justifycenter'),
23162                 btn('justifyright')
23163             );
23164         };
23165
23166         //if(!Roo.isSafari){
23167             if(!this.disable.links){
23168                 tb.add(
23169                     '-',
23170                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
23171                 );
23172             };
23173
23174             if(!this.disable.lists){
23175                 tb.add(
23176                     '-',
23177                     btn('insertorderedlist'),
23178                     btn('insertunorderedlist')
23179                 );
23180             }
23181             if(!this.disable.sourceEdit){
23182                 tb.add(
23183                     '-',
23184                     btn('sourceedit', true, function(btn){
23185                         this.toggleSourceEdit(btn.pressed);
23186                     })
23187                 );
23188             }
23189         //}
23190         
23191         var smenu = { };
23192         // special menu.. - needs to be tidied up..
23193         if (!this.disable.special) {
23194             smenu = {
23195                 text: "&#169;",
23196                 cls: 'x-edit-none',
23197                 
23198                 menu : {
23199                     items : []
23200                 }
23201             };
23202             for (var i =0; i < this.specialChars.length; i++) {
23203                 smenu.menu.items.push({
23204                     
23205                     html: this.specialChars[i],
23206                     handler: function(a,b) {
23207                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
23208                         //editor.insertAtCursor(a.html);
23209                         
23210                     },
23211                     tabIndex:-1
23212                 });
23213             }
23214             
23215             
23216             tb.add(smenu);
23217             
23218             
23219         }
23220         
23221         var cmenu = { };
23222         if (!this.disable.cleanStyles) {
23223             cmenu = {
23224                 cls: 'x-btn-icon x-btn-clear',
23225                 
23226                 menu : {
23227                     items : []
23228                 }
23229             };
23230             for (var i =0; i < this.cleanStyles.length; i++) {
23231                 cmenu.menu.items.push({
23232                     actiontype : this.cleanStyles[i],
23233                     html: 'Remove ' + this.cleanStyles[i],
23234                     handler: function(a,b) {
23235 //                        Roo.log(a);
23236 //                        Roo.log(b);
23237                         var c = Roo.get(editorcore.doc.body);
23238                         c.select('[style]').each(function(s) {
23239                             s.dom.style.removeProperty(a.actiontype);
23240                         });
23241                         editorcore.syncValue();
23242                     },
23243                     tabIndex:-1
23244                 });
23245             }
23246              cmenu.menu.items.push({
23247                 actiontype : 'tablewidths',
23248                 html: 'Remove Table Widths',
23249                 handler: function(a,b) {
23250                     editorcore.cleanTableWidths();
23251                     editorcore.syncValue();
23252                 },
23253                 tabIndex:-1
23254             });
23255             cmenu.menu.items.push({
23256                 actiontype : 'word',
23257                 html: 'Remove MS Word Formating',
23258                 handler: function(a,b) {
23259                     editorcore.cleanWord();
23260                     editorcore.syncValue();
23261                 },
23262                 tabIndex:-1
23263             });
23264             
23265             cmenu.menu.items.push({
23266                 actiontype : 'all',
23267                 html: 'Remove All Styles',
23268                 handler: function(a,b) {
23269                     
23270                     var c = Roo.get(editorcore.doc.body);
23271                     c.select('[style]').each(function(s) {
23272                         s.dom.removeAttribute('style');
23273                     });
23274                     editorcore.syncValue();
23275                 },
23276                 tabIndex:-1
23277             });
23278             
23279             cmenu.menu.items.push({
23280                 actiontype : 'all',
23281                 html: 'Remove All CSS Classes',
23282                 handler: function(a,b) {
23283                     
23284                     var c = Roo.get(editorcore.doc.body);
23285                     c.select('[class]').each(function(s) {
23286                         s.dom.removeAttribute('class');
23287                     });
23288                     editorcore.cleanWord();
23289                     editorcore.syncValue();
23290                 },
23291                 tabIndex:-1
23292             });
23293             
23294              cmenu.menu.items.push({
23295                 actiontype : 'tidy',
23296                 html: 'Tidy HTML Source',
23297                 handler: function(a,b) {
23298                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
23299                     editorcore.syncValue();
23300                 },
23301                 tabIndex:-1
23302             });
23303             
23304             
23305             tb.add(cmenu);
23306         }
23307          
23308         if (!this.disable.specialElements) {
23309             var semenu = {
23310                 text: "Other;",
23311                 cls: 'x-edit-none',
23312                 menu : {
23313                     items : []
23314                 }
23315             };
23316             for (var i =0; i < this.specialElements.length; i++) {
23317                 semenu.menu.items.push(
23318                     Roo.apply({ 
23319                         handler: function(a,b) {
23320                             editor.insertAtCursor(this.ihtml);
23321                         }
23322                     }, this.specialElements[i])
23323                 );
23324                     
23325             }
23326             
23327             tb.add(semenu);
23328             
23329             
23330         }
23331          
23332         
23333         if (this.btns) {
23334             for(var i =0; i< this.btns.length;i++) {
23335                 var b = Roo.factory(this.btns[i],Roo.form);
23336                 b.cls =  'x-edit-none';
23337                 
23338                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
23339                     b.cls += ' x-init-enable';
23340                 }
23341                 
23342                 b.scope = editorcore;
23343                 tb.add(b);
23344             }
23345         
23346         }
23347         
23348         
23349         
23350         // disable everything...
23351         
23352         this.tb.items.each(function(item){
23353             
23354            if(
23355                 item.id != editorcore.frameId+ '-sourceedit' && 
23356                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
23357             ){
23358                 
23359                 item.disable();
23360             }
23361         });
23362         this.rendered = true;
23363         
23364         // the all the btns;
23365         editor.on('editorevent', this.updateToolbar, this);
23366         // other toolbars need to implement this..
23367         //editor.on('editmodechange', this.updateToolbar, this);
23368     },
23369     
23370     
23371     relayBtnCmd : function(btn) {
23372         this.editorcore.relayCmd(btn.cmd);
23373     },
23374     // private used internally
23375     createLink : function(){
23376         Roo.log("create link?");
23377         var url = prompt(this.createLinkText, this.defaultLinkValue);
23378         if(url && url != 'http:/'+'/'){
23379             this.editorcore.relayCmd('createlink', url);
23380         }
23381     },
23382
23383     
23384     /**
23385      * Protected method that will not generally be called directly. It triggers
23386      * a toolbar update by reading the markup state of the current selection in the editor.
23387      */
23388     updateToolbar: function(){
23389
23390         if(!this.editorcore.activated){
23391             this.editor.onFirstFocus();
23392             return;
23393         }
23394
23395         var btns = this.tb.items.map, 
23396             doc = this.editorcore.doc,
23397             frameId = this.editorcore.frameId;
23398
23399         if(!this.disable.font && !Roo.isSafari){
23400             /*
23401             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
23402             if(name != this.fontSelect.dom.value){
23403                 this.fontSelect.dom.value = name;
23404             }
23405             */
23406         }
23407         if(!this.disable.format){
23408             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
23409             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
23410             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
23411             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
23412         }
23413         if(!this.disable.alignments){
23414             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
23415             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
23416             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
23417         }
23418         if(!Roo.isSafari && !this.disable.lists){
23419             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
23420             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
23421         }
23422         
23423         var ans = this.editorcore.getAllAncestors();
23424         if (this.formatCombo) {
23425             
23426             
23427             var store = this.formatCombo.store;
23428             this.formatCombo.setValue("");
23429             for (var i =0; i < ans.length;i++) {
23430                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23431                     // select it..
23432                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23433                     break;
23434                 }
23435             }
23436         }
23437         
23438         
23439         
23440         // hides menus... - so this cant be on a menu...
23441         Roo.menu.MenuMgr.hideAll();
23442
23443         //this.editorsyncValue();
23444     },
23445    
23446     
23447     createFontOptions : function(){
23448         var buf = [], fs = this.fontFamilies, ff, lc;
23449         
23450         
23451         
23452         for(var i = 0, len = fs.length; i< len; i++){
23453             ff = fs[i];
23454             lc = ff.toLowerCase();
23455             buf.push(
23456                 '<option value="',lc,'" style="font-family:',ff,';"',
23457                     (this.defaultFont == lc ? ' selected="true">' : '>'),
23458                     ff,
23459                 '</option>'
23460             );
23461         }
23462         return buf.join('');
23463     },
23464     
23465     toggleSourceEdit : function(sourceEditMode){
23466         
23467         Roo.log("toolbar toogle");
23468         if(sourceEditMode === undefined){
23469             sourceEditMode = !this.sourceEditMode;
23470         }
23471         this.sourceEditMode = sourceEditMode === true;
23472         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
23473         // just toggle the button?
23474         if(btn.pressed !== this.sourceEditMode){
23475             btn.toggle(this.sourceEditMode);
23476             return;
23477         }
23478         
23479         if(sourceEditMode){
23480             Roo.log("disabling buttons");
23481             this.tb.items.each(function(item){
23482                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
23483                     item.disable();
23484                 }
23485             });
23486           
23487         }else{
23488             Roo.log("enabling buttons");
23489             if(this.editorcore.initialized){
23490                 this.tb.items.each(function(item){
23491                     item.enable();
23492                 });
23493             }
23494             
23495         }
23496         Roo.log("calling toggole on editor");
23497         // tell the editor that it's been pressed..
23498         this.editor.toggleSourceEdit(sourceEditMode);
23499        
23500     },
23501      /**
23502      * Object collection of toolbar tooltips for the buttons in the editor. The key
23503      * is the command id associated with that button and the value is a valid QuickTips object.
23504      * For example:
23505 <pre><code>
23506 {
23507     bold : {
23508         title: 'Bold (Ctrl+B)',
23509         text: 'Make the selected text bold.',
23510         cls: 'x-html-editor-tip'
23511     },
23512     italic : {
23513         title: 'Italic (Ctrl+I)',
23514         text: 'Make the selected text italic.',
23515         cls: 'x-html-editor-tip'
23516     },
23517     ...
23518 </code></pre>
23519     * @type Object
23520      */
23521     buttonTips : {
23522         bold : {
23523             title: 'Bold (Ctrl+B)',
23524             text: 'Make the selected text bold.',
23525             cls: 'x-html-editor-tip'
23526         },
23527         italic : {
23528             title: 'Italic (Ctrl+I)',
23529             text: 'Make the selected text italic.',
23530             cls: 'x-html-editor-tip'
23531         },
23532         underline : {
23533             title: 'Underline (Ctrl+U)',
23534             text: 'Underline the selected text.',
23535             cls: 'x-html-editor-tip'
23536         },
23537         strikethrough : {
23538             title: 'Strikethrough',
23539             text: 'Strikethrough the selected text.',
23540             cls: 'x-html-editor-tip'
23541         },
23542         increasefontsize : {
23543             title: 'Grow Text',
23544             text: 'Increase the font size.',
23545             cls: 'x-html-editor-tip'
23546         },
23547         decreasefontsize : {
23548             title: 'Shrink Text',
23549             text: 'Decrease the font size.',
23550             cls: 'x-html-editor-tip'
23551         },
23552         backcolor : {
23553             title: 'Text Highlight Color',
23554             text: 'Change the background color of the selected text.',
23555             cls: 'x-html-editor-tip'
23556         },
23557         forecolor : {
23558             title: 'Font Color',
23559             text: 'Change the color of the selected text.',
23560             cls: 'x-html-editor-tip'
23561         },
23562         justifyleft : {
23563             title: 'Align Text Left',
23564             text: 'Align text to the left.',
23565             cls: 'x-html-editor-tip'
23566         },
23567         justifycenter : {
23568             title: 'Center Text',
23569             text: 'Center text in the editor.',
23570             cls: 'x-html-editor-tip'
23571         },
23572         justifyright : {
23573             title: 'Align Text Right',
23574             text: 'Align text to the right.',
23575             cls: 'x-html-editor-tip'
23576         },
23577         insertunorderedlist : {
23578             title: 'Bullet List',
23579             text: 'Start a bulleted list.',
23580             cls: 'x-html-editor-tip'
23581         },
23582         insertorderedlist : {
23583             title: 'Numbered List',
23584             text: 'Start a numbered list.',
23585             cls: 'x-html-editor-tip'
23586         },
23587         createlink : {
23588             title: 'Hyperlink',
23589             text: 'Make the selected text a hyperlink.',
23590             cls: 'x-html-editor-tip'
23591         },
23592         sourceedit : {
23593             title: 'Source Edit',
23594             text: 'Switch to source editing mode.',
23595             cls: 'x-html-editor-tip'
23596         }
23597     },
23598     // private
23599     onDestroy : function(){
23600         if(this.rendered){
23601             
23602             this.tb.items.each(function(item){
23603                 if(item.menu){
23604                     item.menu.removeAll();
23605                     if(item.menu.el){
23606                         item.menu.el.destroy();
23607                     }
23608                 }
23609                 item.destroy();
23610             });
23611              
23612         }
23613     },
23614     onFirstFocus: function() {
23615         this.tb.items.each(function(item){
23616            item.enable();
23617         });
23618     }
23619 });
23620
23621
23622
23623
23624 // <script type="text/javascript">
23625 /*
23626  * Based on
23627  * Ext JS Library 1.1.1
23628  * Copyright(c) 2006-2007, Ext JS, LLC.
23629  *  
23630  
23631  */
23632
23633  
23634 /**
23635  * @class Roo.form.HtmlEditor.ToolbarContext
23636  * Context Toolbar
23637  * 
23638  * Usage:
23639  *
23640  new Roo.form.HtmlEditor({
23641     ....
23642     toolbars : [
23643         { xtype: 'ToolbarStandard', styles : {} }
23644         { xtype: 'ToolbarContext', disable : {} }
23645     ]
23646 })
23647
23648      
23649  * 
23650  * @config : {Object} disable List of elements to disable.. (not done yet.)
23651  * @config : {Object} styles  Map of styles available.
23652  * 
23653  */
23654
23655 Roo.form.HtmlEditor.ToolbarContext = function(config)
23656 {
23657     
23658     Roo.apply(this, config);
23659     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23660     // dont call parent... till later.
23661     this.styles = this.styles || {};
23662 }
23663
23664  
23665
23666 Roo.form.HtmlEditor.ToolbarContext.types = {
23667     'IMG' : {
23668         width : {
23669             title: "Width",
23670             width: 40
23671         },
23672         height:  {
23673             title: "Height",
23674             width: 40
23675         },
23676         align: {
23677             title: "Align",
23678             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
23679             width : 80
23680             
23681         },
23682         border: {
23683             title: "Border",
23684             width: 40
23685         },
23686         alt: {
23687             title: "Alt",
23688             width: 120
23689         },
23690         src : {
23691             title: "Src",
23692             width: 220
23693         }
23694         
23695     },
23696     'A' : {
23697         name : {
23698             title: "Name",
23699             width: 50
23700         },
23701         target:  {
23702             title: "Target",
23703             width: 120
23704         },
23705         href:  {
23706             title: "Href",
23707             width: 220
23708         } // border?
23709         
23710     },
23711     'TABLE' : {
23712         rows : {
23713             title: "Rows",
23714             width: 20
23715         },
23716         cols : {
23717             title: "Cols",
23718             width: 20
23719         },
23720         width : {
23721             title: "Width",
23722             width: 40
23723         },
23724         height : {
23725             title: "Height",
23726             width: 40
23727         },
23728         border : {
23729             title: "Border",
23730             width: 20
23731         }
23732     },
23733     'TD' : {
23734         width : {
23735             title: "Width",
23736             width: 40
23737         },
23738         height : {
23739             title: "Height",
23740             width: 40
23741         },   
23742         align: {
23743             title: "Align",
23744             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
23745             width: 80
23746         },
23747         valign: {
23748             title: "Valign",
23749             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
23750             width: 80
23751         },
23752         colspan: {
23753             title: "Colspan",
23754             width: 20
23755             
23756         },
23757          'font-family'  : {
23758             title : "Font",
23759             style : 'fontFamily',
23760             displayField: 'display',
23761             optname : 'font-family',
23762             width: 140
23763         }
23764     },
23765     'INPUT' : {
23766         name : {
23767             title: "name",
23768             width: 120
23769         },
23770         value : {
23771             title: "Value",
23772             width: 120
23773         },
23774         width : {
23775             title: "Width",
23776             width: 40
23777         }
23778     },
23779     'LABEL' : {
23780         'for' : {
23781             title: "For",
23782             width: 120
23783         }
23784     },
23785     'TEXTAREA' : {
23786           name : {
23787             title: "name",
23788             width: 120
23789         },
23790         rows : {
23791             title: "Rows",
23792             width: 20
23793         },
23794         cols : {
23795             title: "Cols",
23796             width: 20
23797         }
23798     },
23799     'SELECT' : {
23800         name : {
23801             title: "name",
23802             width: 120
23803         },
23804         selectoptions : {
23805             title: "Options",
23806             width: 200
23807         }
23808     },
23809     
23810     // should we really allow this??
23811     // should this just be 
23812     'BODY' : {
23813         title : {
23814             title: "Title",
23815             width: 200,
23816             disabled : true
23817         }
23818     },
23819     'SPAN' : {
23820         'font-family'  : {
23821             title : "Font",
23822             style : 'fontFamily',
23823             displayField: 'display',
23824             optname : 'font-family',
23825             width: 140
23826         }
23827     },
23828     'DIV' : {
23829         'font-family'  : {
23830             title : "Font",
23831             style : 'fontFamily',
23832             displayField: 'display',
23833             optname : 'font-family',
23834             width: 140
23835         }
23836     },
23837      'P' : {
23838         'font-family'  : {
23839             title : "Font",
23840             style : 'fontFamily',
23841             displayField: 'display',
23842             optname : 'font-family',
23843             width: 140
23844         }
23845     },
23846     
23847     '*' : {
23848         // empty..
23849     }
23850
23851 };
23852
23853 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
23854 Roo.form.HtmlEditor.ToolbarContext.stores = false;
23855
23856 Roo.form.HtmlEditor.ToolbarContext.options = {
23857         'font-family'  : [ 
23858                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
23859                 [ 'Courier New', 'Courier New'],
23860                 [ 'Tahoma', 'Tahoma'],
23861                 [ 'Times New Roman,serif', 'Times'],
23862                 [ 'Verdana','Verdana' ]
23863         ]
23864 };
23865
23866 // fixme - these need to be configurable..
23867  
23868
23869 //Roo.form.HtmlEditor.ToolbarContext.types
23870
23871
23872 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
23873     
23874     tb: false,
23875     
23876     rendered: false,
23877     
23878     editor : false,
23879     editorcore : false,
23880     /**
23881      * @cfg {Object} disable  List of toolbar elements to disable
23882          
23883      */
23884     disable : false,
23885     /**
23886      * @cfg {Object} styles List of styles 
23887      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
23888      *
23889      * These must be defined in the page, so they get rendered correctly..
23890      * .headline { }
23891      * TD.underline { }
23892      * 
23893      */
23894     styles : false,
23895     
23896     options: false,
23897     
23898     toolbars : false,
23899     
23900     init : function(editor)
23901     {
23902         this.editor = editor;
23903         this.editorcore = editor.editorcore ? editor.editorcore : editor;
23904         var editorcore = this.editorcore;
23905         
23906         var fid = editorcore.frameId;
23907         var etb = this;
23908         function btn(id, toggle, handler){
23909             var xid = fid + '-'+ id ;
23910             return {
23911                 id : xid,
23912                 cmd : id,
23913                 cls : 'x-btn-icon x-edit-'+id,
23914                 enableToggle:toggle !== false,
23915                 scope: editorcore, // was editor...
23916                 handler:handler||editorcore.relayBtnCmd,
23917                 clickEvent:'mousedown',
23918                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
23919                 tabIndex:-1
23920             };
23921         }
23922         // create a new element.
23923         var wdiv = editor.wrap.createChild({
23924                 tag: 'div'
23925             }, editor.wrap.dom.firstChild.nextSibling, true);
23926         
23927         // can we do this more than once??
23928         
23929          // stop form submits
23930       
23931  
23932         // disable everything...
23933         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
23934         this.toolbars = {};
23935            
23936         for (var i in  ty) {
23937           
23938             this.toolbars[i] = this.buildToolbar(ty[i],i);
23939         }
23940         this.tb = this.toolbars.BODY;
23941         this.tb.el.show();
23942         this.buildFooter();
23943         this.footer.show();
23944         editor.on('hide', function( ) { this.footer.hide() }, this);
23945         editor.on('show', function( ) { this.footer.show() }, this);
23946         
23947          
23948         this.rendered = true;
23949         
23950         // the all the btns;
23951         editor.on('editorevent', this.updateToolbar, this);
23952         // other toolbars need to implement this..
23953         //editor.on('editmodechange', this.updateToolbar, this);
23954     },
23955     
23956     
23957     
23958     /**
23959      * Protected method that will not generally be called directly. It triggers
23960      * a toolbar update by reading the markup state of the current selection in the editor.
23961      *
23962      * Note you can force an update by calling on('editorevent', scope, false)
23963      */
23964     updateToolbar: function(editor,ev,sel){
23965
23966         //Roo.log(ev);
23967         // capture mouse up - this is handy for selecting images..
23968         // perhaps should go somewhere else...
23969         if(!this.editorcore.activated){
23970              this.editor.onFirstFocus();
23971             return;
23972         }
23973         
23974         
23975         
23976         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
23977         // selectNode - might want to handle IE?
23978         if (ev &&
23979             (ev.type == 'mouseup' || ev.type == 'click' ) &&
23980             ev.target && ev.target.tagName == 'IMG') {
23981             // they have click on an image...
23982             // let's see if we can change the selection...
23983             sel = ev.target;
23984          
23985               var nodeRange = sel.ownerDocument.createRange();
23986             try {
23987                 nodeRange.selectNode(sel);
23988             } catch (e) {
23989                 nodeRange.selectNodeContents(sel);
23990             }
23991             //nodeRange.collapse(true);
23992             var s = this.editorcore.win.getSelection();
23993             s.removeAllRanges();
23994             s.addRange(nodeRange);
23995         }  
23996         
23997       
23998         var updateFooter = sel ? false : true;
23999         
24000         
24001         var ans = this.editorcore.getAllAncestors();
24002         
24003         // pick
24004         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
24005         
24006         if (!sel) { 
24007             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
24008             sel = sel ? sel : this.editorcore.doc.body;
24009             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
24010             
24011         }
24012         // pick a menu that exists..
24013         var tn = sel.tagName.toUpperCase();
24014         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
24015         
24016         tn = sel.tagName.toUpperCase();
24017         
24018         var lastSel = this.tb.selectedNode;
24019         
24020         this.tb.selectedNode = sel;
24021         
24022         // if current menu does not match..
24023         
24024         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
24025                 
24026             this.tb.el.hide();
24027             ///console.log("show: " + tn);
24028             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
24029             this.tb.el.show();
24030             // update name
24031             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
24032             
24033             
24034             // update attributes
24035             if (this.tb.fields) {
24036                 this.tb.fields.each(function(e) {
24037                     if (e.stylename) {
24038                         e.setValue(sel.style[e.stylename]);
24039                         return;
24040                     } 
24041                    e.setValue(sel.getAttribute(e.attrname));
24042                 });
24043             }
24044             
24045             var hasStyles = false;
24046             for(var i in this.styles) {
24047                 hasStyles = true;
24048                 break;
24049             }
24050             
24051             // update styles
24052             if (hasStyles) { 
24053                 var st = this.tb.fields.item(0);
24054                 
24055                 st.store.removeAll();
24056                
24057                 
24058                 var cn = sel.className.split(/\s+/);
24059                 
24060                 var avs = [];
24061                 if (this.styles['*']) {
24062                     
24063                     Roo.each(this.styles['*'], function(v) {
24064                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24065                     });
24066                 }
24067                 if (this.styles[tn]) { 
24068                     Roo.each(this.styles[tn], function(v) {
24069                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
24070                     });
24071                 }
24072                 
24073                 st.store.loadData(avs);
24074                 st.collapse();
24075                 st.setValue(cn);
24076             }
24077             // flag our selected Node.
24078             this.tb.selectedNode = sel;
24079            
24080            
24081             Roo.menu.MenuMgr.hideAll();
24082
24083         }
24084         
24085         if (!updateFooter) {
24086             //this.footDisp.dom.innerHTML = ''; 
24087             return;
24088         }
24089         // update the footer
24090         //
24091         var html = '';
24092         
24093         this.footerEls = ans.reverse();
24094         Roo.each(this.footerEls, function(a,i) {
24095             if (!a) { return; }
24096             html += html.length ? ' &gt; '  :  '';
24097             
24098             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
24099             
24100         });
24101        
24102         // 
24103         var sz = this.footDisp.up('td').getSize();
24104         this.footDisp.dom.style.width = (sz.width -10) + 'px';
24105         this.footDisp.dom.style.marginLeft = '5px';
24106         
24107         this.footDisp.dom.style.overflow = 'hidden';
24108         
24109         this.footDisp.dom.innerHTML = html;
24110             
24111         //this.editorsyncValue();
24112     },
24113      
24114     
24115    
24116        
24117     // private
24118     onDestroy : function(){
24119         if(this.rendered){
24120             
24121             this.tb.items.each(function(item){
24122                 if(item.menu){
24123                     item.menu.removeAll();
24124                     if(item.menu.el){
24125                         item.menu.el.destroy();
24126                     }
24127                 }
24128                 item.destroy();
24129             });
24130              
24131         }
24132     },
24133     onFirstFocus: function() {
24134         // need to do this for all the toolbars..
24135         this.tb.items.each(function(item){
24136            item.enable();
24137         });
24138     },
24139     buildToolbar: function(tlist, nm)
24140     {
24141         var editor = this.editor;
24142         var editorcore = this.editorcore;
24143          // create a new element.
24144         var wdiv = editor.wrap.createChild({
24145                 tag: 'div'
24146             }, editor.wrap.dom.firstChild.nextSibling, true);
24147         
24148        
24149         var tb = new Roo.Toolbar(wdiv);
24150         // add the name..
24151         
24152         tb.add(nm+ ":&nbsp;");
24153         
24154         var styles = [];
24155         for(var i in this.styles) {
24156             styles.push(i);
24157         }
24158         
24159         // styles...
24160         if (styles && styles.length) {
24161             
24162             // this needs a multi-select checkbox...
24163             tb.addField( new Roo.form.ComboBox({
24164                 store: new Roo.data.SimpleStore({
24165                     id : 'val',
24166                     fields: ['val', 'selected'],
24167                     data : [] 
24168                 }),
24169                 name : '-roo-edit-className',
24170                 attrname : 'className',
24171                 displayField: 'val',
24172                 typeAhead: false,
24173                 mode: 'local',
24174                 editable : false,
24175                 triggerAction: 'all',
24176                 emptyText:'Select Style',
24177                 selectOnFocus:true,
24178                 width: 130,
24179                 listeners : {
24180                     'select': function(c, r, i) {
24181                         // initial support only for on class per el..
24182                         tb.selectedNode.className =  r ? r.get('val') : '';
24183                         editorcore.syncValue();
24184                     }
24185                 }
24186     
24187             }));
24188         }
24189         
24190         var tbc = Roo.form.HtmlEditor.ToolbarContext;
24191         var tbops = tbc.options;
24192         
24193         for (var i in tlist) {
24194             
24195             var item = tlist[i];
24196             tb.add(item.title + ":&nbsp;");
24197             
24198             
24199             //optname == used so you can configure the options available..
24200             var opts = item.opts ? item.opts : false;
24201             if (item.optname) {
24202                 opts = tbops[item.optname];
24203            
24204             }
24205             
24206             if (opts) {
24207                 // opts == pulldown..
24208                 tb.addField( new Roo.form.ComboBox({
24209                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
24210                         id : 'val',
24211                         fields: ['val', 'display'],
24212                         data : opts  
24213                     }),
24214                     name : '-roo-edit-' + i,
24215                     attrname : i,
24216                     stylename : item.style ? item.style : false,
24217                     displayField: item.displayField ? item.displayField : 'val',
24218                     valueField :  'val',
24219                     typeAhead: false,
24220                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
24221                     editable : false,
24222                     triggerAction: 'all',
24223                     emptyText:'Select',
24224                     selectOnFocus:true,
24225                     width: item.width ? item.width  : 130,
24226                     listeners : {
24227                         'select': function(c, r, i) {
24228                             if (c.stylename) {
24229                                 tb.selectedNode.style[c.stylename] =  r.get('val');
24230                                 return;
24231                             }
24232                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
24233                         }
24234                     }
24235
24236                 }));
24237                 continue;
24238                     
24239                  
24240                 
24241                 tb.addField( new Roo.form.TextField({
24242                     name: i,
24243                     width: 100,
24244                     //allowBlank:false,
24245                     value: ''
24246                 }));
24247                 continue;
24248             }
24249             tb.addField( new Roo.form.TextField({
24250                 name: '-roo-edit-' + i,
24251                 attrname : i,
24252                 
24253                 width: item.width,
24254                 //allowBlank:true,
24255                 value: '',
24256                 listeners: {
24257                     'change' : function(f, nv, ov) {
24258                         tb.selectedNode.setAttribute(f.attrname, nv);
24259                         editorcore.syncValue();
24260                     }
24261                 }
24262             }));
24263              
24264         }
24265         
24266         var _this = this;
24267         
24268         if(nm == 'BODY'){
24269             tb.addSeparator();
24270         
24271             tb.addButton( {
24272                 text: 'Stylesheets',
24273
24274                 listeners : {
24275                     click : function ()
24276                     {
24277                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
24278                     }
24279                 }
24280             });
24281         }
24282         
24283         tb.addFill();
24284         tb.addButton( {
24285             text: 'Remove Tag',
24286     
24287             listeners : {
24288                 click : function ()
24289                 {
24290                     // remove
24291                     // undo does not work.
24292                      
24293                     var sn = tb.selectedNode;
24294                     
24295                     var pn = sn.parentNode;
24296                     
24297                     var stn =  sn.childNodes[0];
24298                     var en = sn.childNodes[sn.childNodes.length - 1 ];
24299                     while (sn.childNodes.length) {
24300                         var node = sn.childNodes[0];
24301                         sn.removeChild(node);
24302                         //Roo.log(node);
24303                         pn.insertBefore(node, sn);
24304                         
24305                     }
24306                     pn.removeChild(sn);
24307                     var range = editorcore.createRange();
24308         
24309                     range.setStart(stn,0);
24310                     range.setEnd(en,0); //????
24311                     //range.selectNode(sel);
24312                     
24313                     
24314                     var selection = editorcore.getSelection();
24315                     selection.removeAllRanges();
24316                     selection.addRange(range);
24317                     
24318                     
24319                     
24320                     //_this.updateToolbar(null, null, pn);
24321                     _this.updateToolbar(null, null, null);
24322                     _this.footDisp.dom.innerHTML = ''; 
24323                 }
24324             }
24325             
24326                     
24327                 
24328             
24329         });
24330         
24331         
24332         tb.el.on('click', function(e){
24333             e.preventDefault(); // what does this do?
24334         });
24335         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
24336         tb.el.hide();
24337         tb.name = nm;
24338         // dont need to disable them... as they will get hidden
24339         return tb;
24340          
24341         
24342     },
24343     buildFooter : function()
24344     {
24345         
24346         var fel = this.editor.wrap.createChild();
24347         this.footer = new Roo.Toolbar(fel);
24348         // toolbar has scrolly on left / right?
24349         var footDisp= new Roo.Toolbar.Fill();
24350         var _t = this;
24351         this.footer.add(
24352             {
24353                 text : '&lt;',
24354                 xtype: 'Button',
24355                 handler : function() {
24356                     _t.footDisp.scrollTo('left',0,true)
24357                 }
24358             }
24359         );
24360         this.footer.add( footDisp );
24361         this.footer.add( 
24362             {
24363                 text : '&gt;',
24364                 xtype: 'Button',
24365                 handler : function() {
24366                     // no animation..
24367                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
24368                 }
24369             }
24370         );
24371         var fel = Roo.get(footDisp.el);
24372         fel.addClass('x-editor-context');
24373         this.footDispWrap = fel; 
24374         this.footDispWrap.overflow  = 'hidden';
24375         
24376         this.footDisp = fel.createChild();
24377         this.footDispWrap.on('click', this.onContextClick, this)
24378         
24379         
24380     },
24381     onContextClick : function (ev,dom)
24382     {
24383         ev.preventDefault();
24384         var  cn = dom.className;
24385         //Roo.log(cn);
24386         if (!cn.match(/x-ed-loc-/)) {
24387             return;
24388         }
24389         var n = cn.split('-').pop();
24390         var ans = this.footerEls;
24391         var sel = ans[n];
24392         
24393          // pick
24394         var range = this.editorcore.createRange();
24395         
24396         range.selectNodeContents(sel);
24397         //range.selectNode(sel);
24398         
24399         
24400         var selection = this.editorcore.getSelection();
24401         selection.removeAllRanges();
24402         selection.addRange(range);
24403         
24404         
24405         
24406         this.updateToolbar(null, null, sel);
24407         
24408         
24409     }
24410     
24411     
24412     
24413     
24414     
24415 });
24416
24417
24418
24419
24420
24421 /*
24422  * Based on:
24423  * Ext JS Library 1.1.1
24424  * Copyright(c) 2006-2007, Ext JS, LLC.
24425  *
24426  * Originally Released Under LGPL - original licence link has changed is not relivant.
24427  *
24428  * Fork - LGPL
24429  * <script type="text/javascript">
24430  */
24431  
24432 /**
24433  * @class Roo.form.BasicForm
24434  * @extends Roo.util.Observable
24435  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
24436  * @constructor
24437  * @param {String/HTMLElement/Roo.Element} el The form element or its id
24438  * @param {Object} config Configuration options
24439  */
24440 Roo.form.BasicForm = function(el, config){
24441     this.allItems = [];
24442     this.childForms = [];
24443     Roo.apply(this, config);
24444     /*
24445      * The Roo.form.Field items in this form.
24446      * @type MixedCollection
24447      */
24448      
24449      
24450     this.items = new Roo.util.MixedCollection(false, function(o){
24451         return o.id || (o.id = Roo.id());
24452     });
24453     this.addEvents({
24454         /**
24455          * @event beforeaction
24456          * Fires before any action is performed. Return false to cancel the action.
24457          * @param {Form} this
24458          * @param {Action} action The action to be performed
24459          */
24460         beforeaction: true,
24461         /**
24462          * @event actionfailed
24463          * Fires when an action fails.
24464          * @param {Form} this
24465          * @param {Action} action The action that failed
24466          */
24467         actionfailed : true,
24468         /**
24469          * @event actioncomplete
24470          * Fires when an action is completed.
24471          * @param {Form} this
24472          * @param {Action} action The action that completed
24473          */
24474         actioncomplete : true
24475     });
24476     if(el){
24477         this.initEl(el);
24478     }
24479     Roo.form.BasicForm.superclass.constructor.call(this);
24480     
24481     Roo.form.BasicForm.popover.apply();
24482 };
24483
24484 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
24485     /**
24486      * @cfg {String} method
24487      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
24488      */
24489     /**
24490      * @cfg {DataReader} reader
24491      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
24492      * This is optional as there is built-in support for processing JSON.
24493      */
24494     /**
24495      * @cfg {DataReader} errorReader
24496      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
24497      * This is completely optional as there is built-in support for processing JSON.
24498      */
24499     /**
24500      * @cfg {String} url
24501      * The URL to use for form actions if one isn't supplied in the action options.
24502      */
24503     /**
24504      * @cfg {Boolean} fileUpload
24505      * Set to true if this form is a file upload.
24506      */
24507      
24508     /**
24509      * @cfg {Object} baseParams
24510      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
24511      */
24512      /**
24513      
24514     /**
24515      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
24516      */
24517     timeout: 30,
24518
24519     // private
24520     activeAction : null,
24521
24522     /**
24523      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
24524      * or setValues() data instead of when the form was first created.
24525      */
24526     trackResetOnLoad : false,
24527     
24528     
24529     /**
24530      * childForms - used for multi-tab forms
24531      * @type {Array}
24532      */
24533     childForms : false,
24534     
24535     /**
24536      * allItems - full list of fields.
24537      * @type {Array}
24538      */
24539     allItems : false,
24540     
24541     /**
24542      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
24543      * element by passing it or its id or mask the form itself by passing in true.
24544      * @type Mixed
24545      */
24546     waitMsgTarget : false,
24547     
24548     /**
24549      * @type Boolean
24550      */
24551     disableMask : false,
24552     
24553     /**
24554      * @cfg {Boolean} errorMask (true|false) default false
24555      */
24556     errorMask : false,
24557     
24558     /**
24559      * @cfg {Number} maskOffset Default 100
24560      */
24561     maskOffset : 100,
24562
24563     // private
24564     initEl : function(el){
24565         this.el = Roo.get(el);
24566         this.id = this.el.id || Roo.id();
24567         this.el.on('submit', this.onSubmit, this);
24568         this.el.addClass('x-form');
24569     },
24570
24571     // private
24572     onSubmit : function(e){
24573         e.stopEvent();
24574     },
24575
24576     /**
24577      * Returns true if client-side validation on the form is successful.
24578      * @return Boolean
24579      */
24580     isValid : function(){
24581         var valid = true;
24582         var target = false;
24583         this.items.each(function(f){
24584             if(f.validate()){
24585                 return;
24586             }
24587             
24588             valid = false;
24589                 
24590             if(!target && f.el.isVisible(true)){
24591                 target = f;
24592             }
24593         });
24594         
24595         if(this.errorMask && !valid){
24596             Roo.form.BasicForm.popover.mask(this, target);
24597         }
24598         
24599         return valid;
24600     },
24601
24602     /**
24603      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
24604      * @return Boolean
24605      */
24606     isDirty : function(){
24607         var dirty = false;
24608         this.items.each(function(f){
24609            if(f.isDirty()){
24610                dirty = true;
24611                return false;
24612            }
24613         });
24614         return dirty;
24615     },
24616     
24617     /**
24618      * Returns true if any fields in this form have changed since their original load. (New version)
24619      * @return Boolean
24620      */
24621     
24622     hasChanged : function()
24623     {
24624         var dirty = false;
24625         this.items.each(function(f){
24626            if(f.hasChanged()){
24627                dirty = true;
24628                return false;
24629            }
24630         });
24631         return dirty;
24632         
24633     },
24634     /**
24635      * Resets all hasChanged to 'false' -
24636      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
24637      * So hasChanged storage is only to be used for this purpose
24638      * @return Boolean
24639      */
24640     resetHasChanged : function()
24641     {
24642         this.items.each(function(f){
24643            f.resetHasChanged();
24644         });
24645         
24646     },
24647     
24648     
24649     /**
24650      * Performs a predefined action (submit or load) or custom actions you define on this form.
24651      * @param {String} actionName The name of the action type
24652      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
24653      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
24654      * accept other config options):
24655      * <pre>
24656 Property          Type             Description
24657 ----------------  ---------------  ----------------------------------------------------------------------------------
24658 url               String           The url for the action (defaults to the form's url)
24659 method            String           The form method to use (defaults to the form's method, or POST if not defined)
24660 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
24661 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
24662                                    validate the form on the client (defaults to false)
24663      * </pre>
24664      * @return {BasicForm} this
24665      */
24666     doAction : function(action, options){
24667         if(typeof action == 'string'){
24668             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
24669         }
24670         if(this.fireEvent('beforeaction', this, action) !== false){
24671             this.beforeAction(action);
24672             action.run.defer(100, action);
24673         }
24674         return this;
24675     },
24676
24677     /**
24678      * Shortcut to do a submit action.
24679      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24680      * @return {BasicForm} this
24681      */
24682     submit : function(options){
24683         this.doAction('submit', options);
24684         return this;
24685     },
24686
24687     /**
24688      * Shortcut to do a load action.
24689      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
24690      * @return {BasicForm} this
24691      */
24692     load : function(options){
24693         this.doAction('load', options);
24694         return this;
24695     },
24696
24697     /**
24698      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
24699      * @param {Record} record The record to edit
24700      * @return {BasicForm} this
24701      */
24702     updateRecord : function(record){
24703         record.beginEdit();
24704         var fs = record.fields;
24705         fs.each(function(f){
24706             var field = this.findField(f.name);
24707             if(field){
24708                 record.set(f.name, field.getValue());
24709             }
24710         }, this);
24711         record.endEdit();
24712         return this;
24713     },
24714
24715     /**
24716      * Loads an Roo.data.Record into this form.
24717      * @param {Record} record The record to load
24718      * @return {BasicForm} this
24719      */
24720     loadRecord : function(record){
24721         this.setValues(record.data);
24722         return this;
24723     },
24724
24725     // private
24726     beforeAction : function(action){
24727         var o = action.options;
24728         
24729         if(!this.disableMask) {
24730             if(this.waitMsgTarget === true){
24731                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
24732             }else if(this.waitMsgTarget){
24733                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
24734                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
24735             }else {
24736                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
24737             }
24738         }
24739         
24740          
24741     },
24742
24743     // private
24744     afterAction : function(action, success){
24745         this.activeAction = null;
24746         var o = action.options;
24747         
24748         if(!this.disableMask) {
24749             if(this.waitMsgTarget === true){
24750                 this.el.unmask();
24751             }else if(this.waitMsgTarget){
24752                 this.waitMsgTarget.unmask();
24753             }else{
24754                 Roo.MessageBox.updateProgress(1);
24755                 Roo.MessageBox.hide();
24756             }
24757         }
24758         
24759         if(success){
24760             if(o.reset){
24761                 this.reset();
24762             }
24763             Roo.callback(o.success, o.scope, [this, action]);
24764             this.fireEvent('actioncomplete', this, action);
24765             
24766         }else{
24767             
24768             // failure condition..
24769             // we have a scenario where updates need confirming.
24770             // eg. if a locking scenario exists..
24771             // we look for { errors : { needs_confirm : true }} in the response.
24772             if (
24773                 (typeof(action.result) != 'undefined')  &&
24774                 (typeof(action.result.errors) != 'undefined')  &&
24775                 (typeof(action.result.errors.needs_confirm) != 'undefined')
24776            ){
24777                 var _t = this;
24778                 Roo.MessageBox.confirm(
24779                     "Change requires confirmation",
24780                     action.result.errorMsg,
24781                     function(r) {
24782                         if (r != 'yes') {
24783                             return;
24784                         }
24785                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
24786                     }
24787                     
24788                 );
24789                 
24790                 
24791                 
24792                 return;
24793             }
24794             
24795             Roo.callback(o.failure, o.scope, [this, action]);
24796             // show an error message if no failed handler is set..
24797             if (!this.hasListener('actionfailed')) {
24798                 Roo.MessageBox.alert("Error",
24799                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
24800                         action.result.errorMsg :
24801                         "Saving Failed, please check your entries or try again"
24802                 );
24803             }
24804             
24805             this.fireEvent('actionfailed', this, action);
24806         }
24807         
24808     },
24809
24810     /**
24811      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
24812      * @param {String} id The value to search for
24813      * @return Field
24814      */
24815     findField : function(id){
24816         var field = this.items.get(id);
24817         if(!field){
24818             this.items.each(function(f){
24819                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
24820                     field = f;
24821                     return false;
24822                 }
24823             });
24824         }
24825         return field || null;
24826     },
24827
24828     /**
24829      * Add a secondary form to this one, 
24830      * Used to provide tabbed forms. One form is primary, with hidden values 
24831      * which mirror the elements from the other forms.
24832      * 
24833      * @param {Roo.form.Form} form to add.
24834      * 
24835      */
24836     addForm : function(form)
24837     {
24838        
24839         if (this.childForms.indexOf(form) > -1) {
24840             // already added..
24841             return;
24842         }
24843         this.childForms.push(form);
24844         var n = '';
24845         Roo.each(form.allItems, function (fe) {
24846             
24847             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
24848             if (this.findField(n)) { // already added..
24849                 return;
24850             }
24851             var add = new Roo.form.Hidden({
24852                 name : n
24853             });
24854             add.render(this.el);
24855             
24856             this.add( add );
24857         }, this);
24858         
24859     },
24860     /**
24861      * Mark fields in this form invalid in bulk.
24862      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
24863      * @return {BasicForm} this
24864      */
24865     markInvalid : function(errors){
24866         if(errors instanceof Array){
24867             for(var i = 0, len = errors.length; i < len; i++){
24868                 var fieldError = errors[i];
24869                 var f = this.findField(fieldError.id);
24870                 if(f){
24871                     f.markInvalid(fieldError.msg);
24872                 }
24873             }
24874         }else{
24875             var field, id;
24876             for(id in errors){
24877                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
24878                     field.markInvalid(errors[id]);
24879                 }
24880             }
24881         }
24882         Roo.each(this.childForms || [], function (f) {
24883             f.markInvalid(errors);
24884         });
24885         
24886         return this;
24887     },
24888
24889     /**
24890      * Set values for fields in this form in bulk.
24891      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
24892      * @return {BasicForm} this
24893      */
24894     setValues : function(values){
24895         if(values instanceof Array){ // array of objects
24896             for(var i = 0, len = values.length; i < len; i++){
24897                 var v = values[i];
24898                 var f = this.findField(v.id);
24899                 if(f){
24900                     f.setValue(v.value);
24901                     if(this.trackResetOnLoad){
24902                         f.originalValue = f.getValue();
24903                     }
24904                 }
24905             }
24906         }else{ // object hash
24907             var field, id;
24908             for(id in values){
24909                 if(typeof values[id] != 'function' && (field = this.findField(id))){
24910                     
24911                     if (field.setFromData && 
24912                         field.valueField && 
24913                         field.displayField &&
24914                         // combos' with local stores can 
24915                         // be queried via setValue()
24916                         // to set their value..
24917                         (field.store && !field.store.isLocal)
24918                         ) {
24919                         // it's a combo
24920                         var sd = { };
24921                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
24922                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
24923                         field.setFromData(sd);
24924                         
24925                     } else {
24926                         field.setValue(values[id]);
24927                     }
24928                     
24929                     
24930                     if(this.trackResetOnLoad){
24931                         field.originalValue = field.getValue();
24932                     }
24933                 }
24934             }
24935         }
24936         this.resetHasChanged();
24937         
24938         
24939         Roo.each(this.childForms || [], function (f) {
24940             f.setValues(values);
24941             f.resetHasChanged();
24942         });
24943                 
24944         return this;
24945     },
24946  
24947     /**
24948      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
24949      * they are returned as an array.
24950      * @param {Boolean} asString
24951      * @return {Object}
24952      */
24953     getValues : function(asString){
24954         if (this.childForms) {
24955             // copy values from the child forms
24956             Roo.each(this.childForms, function (f) {
24957                 this.setValues(f.getValues());
24958             }, this);
24959         }
24960         
24961         // use formdata
24962         if (typeof(FormData) != 'undefined' && asString !== true) {
24963             // this relies on a 'recent' version of chrome apparently...
24964             try {
24965                 var fd = (new FormData(this.el.dom)).entries();
24966                 var ret = {};
24967                 var ent = fd.next();
24968                 while (!ent.done) {
24969                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
24970                     ent = fd.next();
24971                 };
24972                 return ret;
24973             } catch(e) {
24974                 
24975             }
24976             
24977         }
24978         
24979         
24980         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
24981         if(asString === true){
24982             return fs;
24983         }
24984         return Roo.urlDecode(fs);
24985     },
24986     
24987     /**
24988      * Returns the fields in this form as an object with key/value pairs. 
24989      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
24990      * @return {Object}
24991      */
24992     getFieldValues : function(with_hidden)
24993     {
24994         if (this.childForms) {
24995             // copy values from the child forms
24996             // should this call getFieldValues - probably not as we do not currently copy
24997             // hidden fields when we generate..
24998             Roo.each(this.childForms, function (f) {
24999                 this.setValues(f.getValues());
25000             }, this);
25001         }
25002         
25003         var ret = {};
25004         this.items.each(function(f){
25005             if (!f.getName()) {
25006                 return;
25007             }
25008             var v = f.getValue();
25009             if (f.inputType =='radio') {
25010                 if (typeof(ret[f.getName()]) == 'undefined') {
25011                     ret[f.getName()] = ''; // empty..
25012                 }
25013                 
25014                 if (!f.el.dom.checked) {
25015                     return;
25016                     
25017                 }
25018                 v = f.el.dom.value;
25019                 
25020             }
25021             
25022             // not sure if this supported any more..
25023             if ((typeof(v) == 'object') && f.getRawValue) {
25024                 v = f.getRawValue() ; // dates..
25025             }
25026             // combo boxes where name != hiddenName...
25027             if (f.name != f.getName()) {
25028                 ret[f.name] = f.getRawValue();
25029             }
25030             ret[f.getName()] = v;
25031         });
25032         
25033         return ret;
25034     },
25035
25036     /**
25037      * Clears all invalid messages in this form.
25038      * @return {BasicForm} this
25039      */
25040     clearInvalid : function(){
25041         this.items.each(function(f){
25042            f.clearInvalid();
25043         });
25044         
25045         Roo.each(this.childForms || [], function (f) {
25046             f.clearInvalid();
25047         });
25048         
25049         
25050         return this;
25051     },
25052
25053     /**
25054      * Resets this form.
25055      * @return {BasicForm} this
25056      */
25057     reset : function(){
25058         this.items.each(function(f){
25059             f.reset();
25060         });
25061         
25062         Roo.each(this.childForms || [], function (f) {
25063             f.reset();
25064         });
25065         this.resetHasChanged();
25066         
25067         return this;
25068     },
25069
25070     /**
25071      * Add Roo.form components to this form.
25072      * @param {Field} field1
25073      * @param {Field} field2 (optional)
25074      * @param {Field} etc (optional)
25075      * @return {BasicForm} this
25076      */
25077     add : function(){
25078         this.items.addAll(Array.prototype.slice.call(arguments, 0));
25079         return this;
25080     },
25081
25082
25083     /**
25084      * Removes a field from the items collection (does NOT remove its markup).
25085      * @param {Field} field
25086      * @return {BasicForm} this
25087      */
25088     remove : function(field){
25089         this.items.remove(field);
25090         return this;
25091     },
25092
25093     /**
25094      * Looks at the fields in this form, checks them for an id attribute,
25095      * and calls applyTo on the existing dom element with that id.
25096      * @return {BasicForm} this
25097      */
25098     render : function(){
25099         this.items.each(function(f){
25100             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
25101                 f.applyTo(f.id);
25102             }
25103         });
25104         return this;
25105     },
25106
25107     /**
25108      * Calls {@link Ext#apply} for all fields in this form with the passed object.
25109      * @param {Object} values
25110      * @return {BasicForm} this
25111      */
25112     applyToFields : function(o){
25113         this.items.each(function(f){
25114            Roo.apply(f, o);
25115         });
25116         return this;
25117     },
25118
25119     /**
25120      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
25121      * @param {Object} values
25122      * @return {BasicForm} this
25123      */
25124     applyIfToFields : function(o){
25125         this.items.each(function(f){
25126            Roo.applyIf(f, o);
25127         });
25128         return this;
25129     }
25130 });
25131
25132 // back compat
25133 Roo.BasicForm = Roo.form.BasicForm;
25134
25135 Roo.apply(Roo.form.BasicForm, {
25136     
25137     popover : {
25138         
25139         padding : 5,
25140         
25141         isApplied : false,
25142         
25143         isMasked : false,
25144         
25145         form : false,
25146         
25147         target : false,
25148         
25149         intervalID : false,
25150         
25151         maskEl : false,
25152         
25153         apply : function()
25154         {
25155             if(this.isApplied){
25156                 return;
25157             }
25158             
25159             this.maskEl = {
25160                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
25161                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
25162                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
25163                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
25164             };
25165             
25166             this.maskEl.top.enableDisplayMode("block");
25167             this.maskEl.left.enableDisplayMode("block");
25168             this.maskEl.bottom.enableDisplayMode("block");
25169             this.maskEl.right.enableDisplayMode("block");
25170             
25171             Roo.get(document.body).on('click', function(){
25172                 this.unmask();
25173             }, this);
25174             
25175             Roo.get(document.body).on('touchstart', function(){
25176                 this.unmask();
25177             }, this);
25178             
25179             this.isApplied = true
25180         },
25181         
25182         mask : function(form, target)
25183         {
25184             this.form = form;
25185             
25186             this.target = target;
25187             
25188             if(!this.form.errorMask || !target.el){
25189                 return;
25190             }
25191             
25192             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
25193             
25194             var ot = this.target.el.calcOffsetsTo(scrollable);
25195             
25196             var scrollTo = ot[1] - this.form.maskOffset;
25197             
25198             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
25199             
25200             scrollable.scrollTo('top', scrollTo);
25201             
25202             var el = this.target.wrap || this.target.el;
25203             
25204             var box = el.getBox();
25205             
25206             this.maskEl.top.setStyle('position', 'absolute');
25207             this.maskEl.top.setStyle('z-index', 10000);
25208             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
25209             this.maskEl.top.setLeft(0);
25210             this.maskEl.top.setTop(0);
25211             this.maskEl.top.show();
25212             
25213             this.maskEl.left.setStyle('position', 'absolute');
25214             this.maskEl.left.setStyle('z-index', 10000);
25215             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
25216             this.maskEl.left.setLeft(0);
25217             this.maskEl.left.setTop(box.y - this.padding);
25218             this.maskEl.left.show();
25219
25220             this.maskEl.bottom.setStyle('position', 'absolute');
25221             this.maskEl.bottom.setStyle('z-index', 10000);
25222             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
25223             this.maskEl.bottom.setLeft(0);
25224             this.maskEl.bottom.setTop(box.bottom + this.padding);
25225             this.maskEl.bottom.show();
25226
25227             this.maskEl.right.setStyle('position', 'absolute');
25228             this.maskEl.right.setStyle('z-index', 10000);
25229             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
25230             this.maskEl.right.setLeft(box.right + this.padding);
25231             this.maskEl.right.setTop(box.y - this.padding);
25232             this.maskEl.right.show();
25233
25234             this.intervalID = window.setInterval(function() {
25235                 Roo.form.BasicForm.popover.unmask();
25236             }, 10000);
25237
25238             window.onwheel = function(){ return false;};
25239             
25240             (function(){ this.isMasked = true; }).defer(500, this);
25241             
25242         },
25243         
25244         unmask : function()
25245         {
25246             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
25247                 return;
25248             }
25249             
25250             this.maskEl.top.setStyle('position', 'absolute');
25251             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
25252             this.maskEl.top.hide();
25253
25254             this.maskEl.left.setStyle('position', 'absolute');
25255             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
25256             this.maskEl.left.hide();
25257
25258             this.maskEl.bottom.setStyle('position', 'absolute');
25259             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
25260             this.maskEl.bottom.hide();
25261
25262             this.maskEl.right.setStyle('position', 'absolute');
25263             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
25264             this.maskEl.right.hide();
25265             
25266             window.onwheel = function(){ return true;};
25267             
25268             if(this.intervalID){
25269                 window.clearInterval(this.intervalID);
25270                 this.intervalID = false;
25271             }
25272             
25273             this.isMasked = false;
25274             
25275         }
25276         
25277     }
25278     
25279 });/*
25280  * Based on:
25281  * Ext JS Library 1.1.1
25282  * Copyright(c) 2006-2007, Ext JS, LLC.
25283  *
25284  * Originally Released Under LGPL - original licence link has changed is not relivant.
25285  *
25286  * Fork - LGPL
25287  * <script type="text/javascript">
25288  */
25289
25290 /**
25291  * @class Roo.form.Form
25292  * @extends Roo.form.BasicForm
25293  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
25294  * @constructor
25295  * @param {Object} config Configuration options
25296  */
25297 Roo.form.Form = function(config){
25298     var xitems =  [];
25299     if (config.items) {
25300         xitems = config.items;
25301         delete config.items;
25302     }
25303    
25304     
25305     Roo.form.Form.superclass.constructor.call(this, null, config);
25306     this.url = this.url || this.action;
25307     if(!this.root){
25308         this.root = new Roo.form.Layout(Roo.applyIf({
25309             id: Roo.id()
25310         }, config));
25311     }
25312     this.active = this.root;
25313     /**
25314      * Array of all the buttons that have been added to this form via {@link addButton}
25315      * @type Array
25316      */
25317     this.buttons = [];
25318     this.allItems = [];
25319     this.addEvents({
25320         /**
25321          * @event clientvalidation
25322          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
25323          * @param {Form} this
25324          * @param {Boolean} valid true if the form has passed client-side validation
25325          */
25326         clientvalidation: true,
25327         /**
25328          * @event rendered
25329          * Fires when the form is rendered
25330          * @param {Roo.form.Form} form
25331          */
25332         rendered : true
25333     });
25334     
25335     if (this.progressUrl) {
25336             // push a hidden field onto the list of fields..
25337             this.addxtype( {
25338                     xns: Roo.form, 
25339                     xtype : 'Hidden', 
25340                     name : 'UPLOAD_IDENTIFIER' 
25341             });
25342         }
25343         
25344     
25345     Roo.each(xitems, this.addxtype, this);
25346     
25347 };
25348
25349 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
25350     /**
25351      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
25352      */
25353     /**
25354      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
25355      */
25356     /**
25357      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
25358      */
25359     buttonAlign:'center',
25360
25361     /**
25362      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
25363      */
25364     minButtonWidth:75,
25365
25366     /**
25367      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
25368      * This property cascades to child containers if not set.
25369      */
25370     labelAlign:'left',
25371
25372     /**
25373      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
25374      * fires a looping event with that state. This is required to bind buttons to the valid
25375      * state using the config value formBind:true on the button.
25376      */
25377     monitorValid : false,
25378
25379     /**
25380      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
25381      */
25382     monitorPoll : 200,
25383     
25384     /**
25385      * @cfg {String} progressUrl - Url to return progress data 
25386      */
25387     
25388     progressUrl : false,
25389     /**
25390      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
25391      * sending a formdata with extra parameters - eg uploaded elements.
25392      */
25393     
25394     formData : false,
25395     
25396     /**
25397      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
25398      * fields are added and the column is closed. If no fields are passed the column remains open
25399      * until end() is called.
25400      * @param {Object} config The config to pass to the column
25401      * @param {Field} field1 (optional)
25402      * @param {Field} field2 (optional)
25403      * @param {Field} etc (optional)
25404      * @return Column The column container object
25405      */
25406     column : function(c){
25407         var col = new Roo.form.Column(c);
25408         this.start(col);
25409         if(arguments.length > 1){ // duplicate code required because of Opera
25410             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25411             this.end();
25412         }
25413         return col;
25414     },
25415
25416     /**
25417      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
25418      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
25419      * until end() is called.
25420      * @param {Object} config The config to pass to the fieldset
25421      * @param {Field} field1 (optional)
25422      * @param {Field} field2 (optional)
25423      * @param {Field} etc (optional)
25424      * @return FieldSet The fieldset container object
25425      */
25426     fieldset : function(c){
25427         var fs = new Roo.form.FieldSet(c);
25428         this.start(fs);
25429         if(arguments.length > 1){ // duplicate code required because of Opera
25430             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25431             this.end();
25432         }
25433         return fs;
25434     },
25435
25436     /**
25437      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
25438      * fields are added and the container is closed. If no fields are passed the container remains open
25439      * until end() is called.
25440      * @param {Object} config The config to pass to the Layout
25441      * @param {Field} field1 (optional)
25442      * @param {Field} field2 (optional)
25443      * @param {Field} etc (optional)
25444      * @return Layout The container object
25445      */
25446     container : function(c){
25447         var l = new Roo.form.Layout(c);
25448         this.start(l);
25449         if(arguments.length > 1){ // duplicate code required because of Opera
25450             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
25451             this.end();
25452         }
25453         return l;
25454     },
25455
25456     /**
25457      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
25458      * @param {Object} container A Roo.form.Layout or subclass of Layout
25459      * @return {Form} this
25460      */
25461     start : function(c){
25462         // cascade label info
25463         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
25464         this.active.stack.push(c);
25465         c.ownerCt = this.active;
25466         this.active = c;
25467         return this;
25468     },
25469
25470     /**
25471      * Closes the current open container
25472      * @return {Form} this
25473      */
25474     end : function(){
25475         if(this.active == this.root){
25476             return this;
25477         }
25478         this.active = this.active.ownerCt;
25479         return this;
25480     },
25481
25482     /**
25483      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
25484      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
25485      * as the label of the field.
25486      * @param {Field} field1
25487      * @param {Field} field2 (optional)
25488      * @param {Field} etc. (optional)
25489      * @return {Form} this
25490      */
25491     add : function(){
25492         this.active.stack.push.apply(this.active.stack, arguments);
25493         this.allItems.push.apply(this.allItems,arguments);
25494         var r = [];
25495         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
25496             if(a[i].isFormField){
25497                 r.push(a[i]);
25498             }
25499         }
25500         if(r.length > 0){
25501             Roo.form.Form.superclass.add.apply(this, r);
25502         }
25503         return this;
25504     },
25505     
25506
25507     
25508     
25509     
25510      /**
25511      * Find any element that has been added to a form, using it's ID or name
25512      * This can include framesets, columns etc. along with regular fields..
25513      * @param {String} id - id or name to find.
25514      
25515      * @return {Element} e - or false if nothing found.
25516      */
25517     findbyId : function(id)
25518     {
25519         var ret = false;
25520         if (!id) {
25521             return ret;
25522         }
25523         Roo.each(this.allItems, function(f){
25524             if (f.id == id || f.name == id ){
25525                 ret = f;
25526                 return false;
25527             }
25528         });
25529         return ret;
25530     },
25531
25532     
25533     
25534     /**
25535      * Render this form into the passed container. This should only be called once!
25536      * @param {String/HTMLElement/Element} container The element this component should be rendered into
25537      * @return {Form} this
25538      */
25539     render : function(ct)
25540     {
25541         
25542         
25543         
25544         ct = Roo.get(ct);
25545         var o = this.autoCreate || {
25546             tag: 'form',
25547             method : this.method || 'POST',
25548             id : this.id || Roo.id()
25549         };
25550         this.initEl(ct.createChild(o));
25551
25552         this.root.render(this.el);
25553         
25554        
25555              
25556         this.items.each(function(f){
25557             f.render('x-form-el-'+f.id);
25558         });
25559
25560         if(this.buttons.length > 0){
25561             // tables are required to maintain order and for correct IE layout
25562             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
25563                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
25564                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
25565             }}, null, true);
25566             var tr = tb.getElementsByTagName('tr')[0];
25567             for(var i = 0, len = this.buttons.length; i < len; i++) {
25568                 var b = this.buttons[i];
25569                 var td = document.createElement('td');
25570                 td.className = 'x-form-btn-td';
25571                 b.render(tr.appendChild(td));
25572             }
25573         }
25574         if(this.monitorValid){ // initialize after render
25575             this.startMonitoring();
25576         }
25577         this.fireEvent('rendered', this);
25578         return this;
25579     },
25580
25581     /**
25582      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
25583      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
25584      * object or a valid Roo.DomHelper element config
25585      * @param {Function} handler The function called when the button is clicked
25586      * @param {Object} scope (optional) The scope of the handler function
25587      * @return {Roo.Button}
25588      */
25589     addButton : function(config, handler, scope){
25590         var bc = {
25591             handler: handler,
25592             scope: scope,
25593             minWidth: this.minButtonWidth,
25594             hideParent:true
25595         };
25596         if(typeof config == "string"){
25597             bc.text = config;
25598         }else{
25599             Roo.apply(bc, config);
25600         }
25601         var btn = new Roo.Button(null, bc);
25602         this.buttons.push(btn);
25603         return btn;
25604     },
25605
25606      /**
25607      * Adds a series of form elements (using the xtype property as the factory method.
25608      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
25609      * @param {Object} config 
25610      */
25611     
25612     addxtype : function()
25613     {
25614         var ar = Array.prototype.slice.call(arguments, 0);
25615         var ret = false;
25616         for(var i = 0; i < ar.length; i++) {
25617             if (!ar[i]) {
25618                 continue; // skip -- if this happends something invalid got sent, we 
25619                 // should ignore it, as basically that interface element will not show up
25620                 // and that should be pretty obvious!!
25621             }
25622             
25623             if (Roo.form[ar[i].xtype]) {
25624                 ar[i].form = this;
25625                 var fe = Roo.factory(ar[i], Roo.form);
25626                 if (!ret) {
25627                     ret = fe;
25628                 }
25629                 fe.form = this;
25630                 if (fe.store) {
25631                     fe.store.form = this;
25632                 }
25633                 if (fe.isLayout) {  
25634                          
25635                     this.start(fe);
25636                     this.allItems.push(fe);
25637                     if (fe.items && fe.addxtype) {
25638                         fe.addxtype.apply(fe, fe.items);
25639                         delete fe.items;
25640                     }
25641                      this.end();
25642                     continue;
25643                 }
25644                 
25645                 
25646                  
25647                 this.add(fe);
25648               //  console.log('adding ' + ar[i].xtype);
25649             }
25650             if (ar[i].xtype == 'Button') {  
25651                 //console.log('adding button');
25652                 //console.log(ar[i]);
25653                 this.addButton(ar[i]);
25654                 this.allItems.push(fe);
25655                 continue;
25656             }
25657             
25658             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
25659                 alert('end is not supported on xtype any more, use items');
25660             //    this.end();
25661             //    //console.log('adding end');
25662             }
25663             
25664         }
25665         return ret;
25666     },
25667     
25668     /**
25669      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
25670      * option "monitorValid"
25671      */
25672     startMonitoring : function(){
25673         if(!this.bound){
25674             this.bound = true;
25675             Roo.TaskMgr.start({
25676                 run : this.bindHandler,
25677                 interval : this.monitorPoll || 200,
25678                 scope: this
25679             });
25680         }
25681     },
25682
25683     /**
25684      * Stops monitoring of the valid state of this form
25685      */
25686     stopMonitoring : function(){
25687         this.bound = false;
25688     },
25689
25690     // private
25691     bindHandler : function(){
25692         if(!this.bound){
25693             return false; // stops binding
25694         }
25695         var valid = true;
25696         this.items.each(function(f){
25697             if(!f.isValid(true)){
25698                 valid = false;
25699                 return false;
25700             }
25701         });
25702         for(var i = 0, len = this.buttons.length; i < len; i++){
25703             var btn = this.buttons[i];
25704             if(btn.formBind === true && btn.disabled === valid){
25705                 btn.setDisabled(!valid);
25706             }
25707         }
25708         this.fireEvent('clientvalidation', this, valid);
25709     }
25710     
25711     
25712     
25713     
25714     
25715     
25716     
25717     
25718 });
25719
25720
25721 // back compat
25722 Roo.Form = Roo.form.Form;
25723 /*
25724  * Based on:
25725  * Ext JS Library 1.1.1
25726  * Copyright(c) 2006-2007, Ext JS, LLC.
25727  *
25728  * Originally Released Under LGPL - original licence link has changed is not relivant.
25729  *
25730  * Fork - LGPL
25731  * <script type="text/javascript">
25732  */
25733
25734 // as we use this in bootstrap.
25735 Roo.namespace('Roo.form');
25736  /**
25737  * @class Roo.form.Action
25738  * Internal Class used to handle form actions
25739  * @constructor
25740  * @param {Roo.form.BasicForm} el The form element or its id
25741  * @param {Object} config Configuration options
25742  */
25743
25744  
25745  
25746 // define the action interface
25747 Roo.form.Action = function(form, options){
25748     this.form = form;
25749     this.options = options || {};
25750 };
25751 /**
25752  * Client Validation Failed
25753  * @const 
25754  */
25755 Roo.form.Action.CLIENT_INVALID = 'client';
25756 /**
25757  * Server Validation Failed
25758  * @const 
25759  */
25760 Roo.form.Action.SERVER_INVALID = 'server';
25761  /**
25762  * Connect to Server Failed
25763  * @const 
25764  */
25765 Roo.form.Action.CONNECT_FAILURE = 'connect';
25766 /**
25767  * Reading Data from Server Failed
25768  * @const 
25769  */
25770 Roo.form.Action.LOAD_FAILURE = 'load';
25771
25772 Roo.form.Action.prototype = {
25773     type : 'default',
25774     failureType : undefined,
25775     response : undefined,
25776     result : undefined,
25777
25778     // interface method
25779     run : function(options){
25780
25781     },
25782
25783     // interface method
25784     success : function(response){
25785
25786     },
25787
25788     // interface method
25789     handleResponse : function(response){
25790
25791     },
25792
25793     // default connection failure
25794     failure : function(response){
25795         
25796         this.response = response;
25797         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25798         this.form.afterAction(this, false);
25799     },
25800
25801     processResponse : function(response){
25802         this.response = response;
25803         if(!response.responseText){
25804             return true;
25805         }
25806         this.result = this.handleResponse(response);
25807         return this.result;
25808     },
25809
25810     // utility functions used internally
25811     getUrl : function(appendParams){
25812         var url = this.options.url || this.form.url || this.form.el.dom.action;
25813         if(appendParams){
25814             var p = this.getParams();
25815             if(p){
25816                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
25817             }
25818         }
25819         return url;
25820     },
25821
25822     getMethod : function(){
25823         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
25824     },
25825
25826     getParams : function(){
25827         var bp = this.form.baseParams;
25828         var p = this.options.params;
25829         if(p){
25830             if(typeof p == "object"){
25831                 p = Roo.urlEncode(Roo.applyIf(p, bp));
25832             }else if(typeof p == 'string' && bp){
25833                 p += '&' + Roo.urlEncode(bp);
25834             }
25835         }else if(bp){
25836             p = Roo.urlEncode(bp);
25837         }
25838         return p;
25839     },
25840
25841     createCallback : function(){
25842         return {
25843             success: this.success,
25844             failure: this.failure,
25845             scope: this,
25846             timeout: (this.form.timeout*1000),
25847             upload: this.form.fileUpload ? this.success : undefined
25848         };
25849     }
25850 };
25851
25852 Roo.form.Action.Submit = function(form, options){
25853     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
25854 };
25855
25856 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
25857     type : 'submit',
25858
25859     haveProgress : false,
25860     uploadComplete : false,
25861     
25862     // uploadProgress indicator.
25863     uploadProgress : function()
25864     {
25865         if (!this.form.progressUrl) {
25866             return;
25867         }
25868         
25869         if (!this.haveProgress) {
25870             Roo.MessageBox.progress("Uploading", "Uploading");
25871         }
25872         if (this.uploadComplete) {
25873            Roo.MessageBox.hide();
25874            return;
25875         }
25876         
25877         this.haveProgress = true;
25878    
25879         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
25880         
25881         var c = new Roo.data.Connection();
25882         c.request({
25883             url : this.form.progressUrl,
25884             params: {
25885                 id : uid
25886             },
25887             method: 'GET',
25888             success : function(req){
25889                //console.log(data);
25890                 var rdata = false;
25891                 var edata;
25892                 try  {
25893                    rdata = Roo.decode(req.responseText)
25894                 } catch (e) {
25895                     Roo.log("Invalid data from server..");
25896                     Roo.log(edata);
25897                     return;
25898                 }
25899                 if (!rdata || !rdata.success) {
25900                     Roo.log(rdata);
25901                     Roo.MessageBox.alert(Roo.encode(rdata));
25902                     return;
25903                 }
25904                 var data = rdata.data;
25905                 
25906                 if (this.uploadComplete) {
25907                    Roo.MessageBox.hide();
25908                    return;
25909                 }
25910                    
25911                 if (data){
25912                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
25913                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
25914                     );
25915                 }
25916                 this.uploadProgress.defer(2000,this);
25917             },
25918        
25919             failure: function(data) {
25920                 Roo.log('progress url failed ');
25921                 Roo.log(data);
25922             },
25923             scope : this
25924         });
25925            
25926     },
25927     
25928     
25929     run : function()
25930     {
25931         // run get Values on the form, so it syncs any secondary forms.
25932         this.form.getValues();
25933         
25934         var o = this.options;
25935         var method = this.getMethod();
25936         var isPost = method == 'POST';
25937         if(o.clientValidation === false || this.form.isValid()){
25938             
25939             if (this.form.progressUrl) {
25940                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
25941                     (new Date() * 1) + '' + Math.random());
25942                     
25943             } 
25944             
25945             
25946             Roo.Ajax.request(Roo.apply(this.createCallback(), {
25947                 form:this.form.el.dom,
25948                 url:this.getUrl(!isPost),
25949                 method: method,
25950                 params:isPost ? this.getParams() : null,
25951                 isUpload: this.form.fileUpload,
25952                 formData : this.form.formData
25953             }));
25954             
25955             this.uploadProgress();
25956
25957         }else if (o.clientValidation !== false){ // client validation failed
25958             this.failureType = Roo.form.Action.CLIENT_INVALID;
25959             this.form.afterAction(this, false);
25960         }
25961     },
25962
25963     success : function(response)
25964     {
25965         this.uploadComplete= true;
25966         if (this.haveProgress) {
25967             Roo.MessageBox.hide();
25968         }
25969         
25970         
25971         var result = this.processResponse(response);
25972         if(result === true || result.success){
25973             this.form.afterAction(this, true);
25974             return;
25975         }
25976         if(result.errors){
25977             this.form.markInvalid(result.errors);
25978             this.failureType = Roo.form.Action.SERVER_INVALID;
25979         }
25980         this.form.afterAction(this, false);
25981     },
25982     failure : function(response)
25983     {
25984         this.uploadComplete= true;
25985         if (this.haveProgress) {
25986             Roo.MessageBox.hide();
25987         }
25988         
25989         this.response = response;
25990         this.failureType = Roo.form.Action.CONNECT_FAILURE;
25991         this.form.afterAction(this, false);
25992     },
25993     
25994     handleResponse : function(response){
25995         if(this.form.errorReader){
25996             var rs = this.form.errorReader.read(response);
25997             var errors = [];
25998             if(rs.records){
25999                 for(var i = 0, len = rs.records.length; i < len; i++) {
26000                     var r = rs.records[i];
26001                     errors[i] = r.data;
26002                 }
26003             }
26004             if(errors.length < 1){
26005                 errors = null;
26006             }
26007             return {
26008                 success : rs.success,
26009                 errors : errors
26010             };
26011         }
26012         var ret = false;
26013         try {
26014             ret = Roo.decode(response.responseText);
26015         } catch (e) {
26016             ret = {
26017                 success: false,
26018                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
26019                 errors : []
26020             };
26021         }
26022         return ret;
26023         
26024     }
26025 });
26026
26027
26028 Roo.form.Action.Load = function(form, options){
26029     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
26030     this.reader = this.form.reader;
26031 };
26032
26033 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
26034     type : 'load',
26035
26036     run : function(){
26037         
26038         Roo.Ajax.request(Roo.apply(
26039                 this.createCallback(), {
26040                     method:this.getMethod(),
26041                     url:this.getUrl(false),
26042                     params:this.getParams()
26043         }));
26044     },
26045
26046     success : function(response){
26047         
26048         var result = this.processResponse(response);
26049         if(result === true || !result.success || !result.data){
26050             this.failureType = Roo.form.Action.LOAD_FAILURE;
26051             this.form.afterAction(this, false);
26052             return;
26053         }
26054         this.form.clearInvalid();
26055         this.form.setValues(result.data);
26056         this.form.afterAction(this, true);
26057     },
26058
26059     handleResponse : function(response){
26060         if(this.form.reader){
26061             var rs = this.form.reader.read(response);
26062             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
26063             return {
26064                 success : rs.success,
26065                 data : data
26066             };
26067         }
26068         return Roo.decode(response.responseText);
26069     }
26070 });
26071
26072 Roo.form.Action.ACTION_TYPES = {
26073     'load' : Roo.form.Action.Load,
26074     'submit' : Roo.form.Action.Submit
26075 };/*
26076  * Based on:
26077  * Ext JS Library 1.1.1
26078  * Copyright(c) 2006-2007, Ext JS, LLC.
26079  *
26080  * Originally Released Under LGPL - original licence link has changed is not relivant.
26081  *
26082  * Fork - LGPL
26083  * <script type="text/javascript">
26084  */
26085  
26086 /**
26087  * @class Roo.form.Layout
26088  * @extends Roo.Component
26089  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
26090  * @constructor
26091  * @param {Object} config Configuration options
26092  */
26093 Roo.form.Layout = function(config){
26094     var xitems = [];
26095     if (config.items) {
26096         xitems = config.items;
26097         delete config.items;
26098     }
26099     Roo.form.Layout.superclass.constructor.call(this, config);
26100     this.stack = [];
26101     Roo.each(xitems, this.addxtype, this);
26102      
26103 };
26104
26105 Roo.extend(Roo.form.Layout, Roo.Component, {
26106     /**
26107      * @cfg {String/Object} autoCreate
26108      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
26109      */
26110     /**
26111      * @cfg {String/Object/Function} style
26112      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
26113      * a function which returns such a specification.
26114      */
26115     /**
26116      * @cfg {String} labelAlign
26117      * Valid values are "left," "top" and "right" (defaults to "left")
26118      */
26119     /**
26120      * @cfg {Number} labelWidth
26121      * Fixed width in pixels of all field labels (defaults to undefined)
26122      */
26123     /**
26124      * @cfg {Boolean} clear
26125      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
26126      */
26127     clear : true,
26128     /**
26129      * @cfg {String} labelSeparator
26130      * The separator to use after field labels (defaults to ':')
26131      */
26132     labelSeparator : ':',
26133     /**
26134      * @cfg {Boolean} hideLabels
26135      * True to suppress the display of field labels in this layout (defaults to false)
26136      */
26137     hideLabels : false,
26138
26139     // private
26140     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
26141     
26142     isLayout : true,
26143     
26144     // private
26145     onRender : function(ct, position){
26146         if(this.el){ // from markup
26147             this.el = Roo.get(this.el);
26148         }else {  // generate
26149             var cfg = this.getAutoCreate();
26150             this.el = ct.createChild(cfg, position);
26151         }
26152         if(this.style){
26153             this.el.applyStyles(this.style);
26154         }
26155         if(this.labelAlign){
26156             this.el.addClass('x-form-label-'+this.labelAlign);
26157         }
26158         if(this.hideLabels){
26159             this.labelStyle = "display:none";
26160             this.elementStyle = "padding-left:0;";
26161         }else{
26162             if(typeof this.labelWidth == 'number'){
26163                 this.labelStyle = "width:"+this.labelWidth+"px;";
26164                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
26165             }
26166             if(this.labelAlign == 'top'){
26167                 this.labelStyle = "width:auto;";
26168                 this.elementStyle = "padding-left:0;";
26169             }
26170         }
26171         var stack = this.stack;
26172         var slen = stack.length;
26173         if(slen > 0){
26174             if(!this.fieldTpl){
26175                 var t = new Roo.Template(
26176                     '<div class="x-form-item {5}">',
26177                         '<label for="{0}" style="{2}">{1}{4}</label>',
26178                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26179                         '</div>',
26180                     '</div><div class="x-form-clear-left"></div>'
26181                 );
26182                 t.disableFormats = true;
26183                 t.compile();
26184                 Roo.form.Layout.prototype.fieldTpl = t;
26185             }
26186             for(var i = 0; i < slen; i++) {
26187                 if(stack[i].isFormField){
26188                     this.renderField(stack[i]);
26189                 }else{
26190                     this.renderComponent(stack[i]);
26191                 }
26192             }
26193         }
26194         if(this.clear){
26195             this.el.createChild({cls:'x-form-clear'});
26196         }
26197     },
26198
26199     // private
26200     renderField : function(f){
26201         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
26202                f.id, //0
26203                f.fieldLabel, //1
26204                f.labelStyle||this.labelStyle||'', //2
26205                this.elementStyle||'', //3
26206                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
26207                f.itemCls||this.itemCls||''  //5
26208        ], true).getPrevSibling());
26209     },
26210
26211     // private
26212     renderComponent : function(c){
26213         c.render(c.isLayout ? this.el : this.el.createChild());    
26214     },
26215     /**
26216      * Adds a object form elements (using the xtype property as the factory method.)
26217      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
26218      * @param {Object} config 
26219      */
26220     addxtype : function(o)
26221     {
26222         // create the lement.
26223         o.form = this.form;
26224         var fe = Roo.factory(o, Roo.form);
26225         this.form.allItems.push(fe);
26226         this.stack.push(fe);
26227         
26228         if (fe.isFormField) {
26229             this.form.items.add(fe);
26230         }
26231          
26232         return fe;
26233     }
26234 });
26235
26236 /**
26237  * @class Roo.form.Column
26238  * @extends Roo.form.Layout
26239  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
26240  * @constructor
26241  * @param {Object} config Configuration options
26242  */
26243 Roo.form.Column = function(config){
26244     Roo.form.Column.superclass.constructor.call(this, config);
26245 };
26246
26247 Roo.extend(Roo.form.Column, Roo.form.Layout, {
26248     /**
26249      * @cfg {Number/String} width
26250      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26251      */
26252     /**
26253      * @cfg {String/Object} autoCreate
26254      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
26255      */
26256
26257     // private
26258     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
26259
26260     // private
26261     onRender : function(ct, position){
26262         Roo.form.Column.superclass.onRender.call(this, ct, position);
26263         if(this.width){
26264             this.el.setWidth(this.width);
26265         }
26266     }
26267 });
26268
26269
26270 /**
26271  * @class Roo.form.Row
26272  * @extends Roo.form.Layout
26273  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
26274  * @constructor
26275  * @param {Object} config Configuration options
26276  */
26277
26278  
26279 Roo.form.Row = function(config){
26280     Roo.form.Row.superclass.constructor.call(this, config);
26281 };
26282  
26283 Roo.extend(Roo.form.Row, Roo.form.Layout, {
26284       /**
26285      * @cfg {Number/String} width
26286      * The fixed width of the column in pixels or CSS value (defaults to "auto")
26287      */
26288     /**
26289      * @cfg {Number/String} height
26290      * The fixed height of the column in pixels or CSS value (defaults to "auto")
26291      */
26292     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
26293     
26294     padWidth : 20,
26295     // private
26296     onRender : function(ct, position){
26297         //console.log('row render');
26298         if(!this.rowTpl){
26299             var t = new Roo.Template(
26300                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
26301                     '<label for="{0}" style="{2}">{1}{4}</label>',
26302                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
26303                     '</div>',
26304                 '</div>'
26305             );
26306             t.disableFormats = true;
26307             t.compile();
26308             Roo.form.Layout.prototype.rowTpl = t;
26309         }
26310         this.fieldTpl = this.rowTpl;
26311         
26312         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
26313         var labelWidth = 100;
26314         
26315         if ((this.labelAlign != 'top')) {
26316             if (typeof this.labelWidth == 'number') {
26317                 labelWidth = this.labelWidth
26318             }
26319             this.padWidth =  20 + labelWidth;
26320             
26321         }
26322         
26323         Roo.form.Column.superclass.onRender.call(this, ct, position);
26324         if(this.width){
26325             this.el.setWidth(this.width);
26326         }
26327         if(this.height){
26328             this.el.setHeight(this.height);
26329         }
26330     },
26331     
26332     // private
26333     renderField : function(f){
26334         f.fieldEl = this.fieldTpl.append(this.el, [
26335                f.id, f.fieldLabel,
26336                f.labelStyle||this.labelStyle||'',
26337                this.elementStyle||'',
26338                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
26339                f.itemCls||this.itemCls||'',
26340                f.width ? f.width + this.padWidth : 160 + this.padWidth
26341        ],true);
26342     }
26343 });
26344  
26345
26346 /**
26347  * @class Roo.form.FieldSet
26348  * @extends Roo.form.Layout
26349  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
26350  * @constructor
26351  * @param {Object} config Configuration options
26352  */
26353 Roo.form.FieldSet = function(config){
26354     Roo.form.FieldSet.superclass.constructor.call(this, config);
26355 };
26356
26357 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
26358     /**
26359      * @cfg {String} legend
26360      * The text to display as the legend for the FieldSet (defaults to '')
26361      */
26362     /**
26363      * @cfg {String/Object} autoCreate
26364      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
26365      */
26366
26367     // private
26368     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
26369
26370     // private
26371     onRender : function(ct, position){
26372         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
26373         if(this.legend){
26374             this.setLegend(this.legend);
26375         }
26376     },
26377
26378     // private
26379     setLegend : function(text){
26380         if(this.rendered){
26381             this.el.child('legend').update(text);
26382         }
26383     }
26384 });/*
26385  * Based on:
26386  * Ext JS Library 1.1.1
26387  * Copyright(c) 2006-2007, Ext JS, LLC.
26388  *
26389  * Originally Released Under LGPL - original licence link has changed is not relivant.
26390  *
26391  * Fork - LGPL
26392  * <script type="text/javascript">
26393  */
26394 /**
26395  * @class Roo.form.VTypes
26396  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
26397  * @singleton
26398  */
26399 Roo.form.VTypes = function(){
26400     // closure these in so they are only created once.
26401     var alpha = /^[a-zA-Z_]+$/;
26402     var alphanum = /^[a-zA-Z0-9_]+$/;
26403     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
26404     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
26405
26406     // All these messages and functions are configurable
26407     return {
26408         /**
26409          * The function used to validate email addresses
26410          * @param {String} value The email address
26411          */
26412         'email' : function(v){
26413             return email.test(v);
26414         },
26415         /**
26416          * The error text to display when the email validation function returns false
26417          * @type String
26418          */
26419         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
26420         /**
26421          * The keystroke filter mask to be applied on email input
26422          * @type RegExp
26423          */
26424         'emailMask' : /[a-z0-9_\.\-@]/i,
26425
26426         /**
26427          * The function used to validate URLs
26428          * @param {String} value The URL
26429          */
26430         'url' : function(v){
26431             return url.test(v);
26432         },
26433         /**
26434          * The error text to display when the url validation function returns false
26435          * @type String
26436          */
26437         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
26438         
26439         /**
26440          * The function used to validate alpha values
26441          * @param {String} value The value
26442          */
26443         'alpha' : function(v){
26444             return alpha.test(v);
26445         },
26446         /**
26447          * The error text to display when the alpha validation function returns false
26448          * @type String
26449          */
26450         'alphaText' : 'This field should only contain letters and _',
26451         /**
26452          * The keystroke filter mask to be applied on alpha input
26453          * @type RegExp
26454          */
26455         'alphaMask' : /[a-z_]/i,
26456
26457         /**
26458          * The function used to validate alphanumeric values
26459          * @param {String} value The value
26460          */
26461         'alphanum' : function(v){
26462             return alphanum.test(v);
26463         },
26464         /**
26465          * The error text to display when the alphanumeric validation function returns false
26466          * @type String
26467          */
26468         'alphanumText' : 'This field should only contain letters, numbers and _',
26469         /**
26470          * The keystroke filter mask to be applied on alphanumeric input
26471          * @type RegExp
26472          */
26473         'alphanumMask' : /[a-z0-9_]/i
26474     };
26475 }();//<script type="text/javascript">
26476
26477 /**
26478  * @class Roo.form.FCKeditor
26479  * @extends Roo.form.TextArea
26480  * Wrapper around the FCKEditor http://www.fckeditor.net
26481  * @constructor
26482  * Creates a new FCKeditor
26483  * @param {Object} config Configuration options
26484  */
26485 Roo.form.FCKeditor = function(config){
26486     Roo.form.FCKeditor.superclass.constructor.call(this, config);
26487     this.addEvents({
26488          /**
26489          * @event editorinit
26490          * Fired when the editor is initialized - you can add extra handlers here..
26491          * @param {FCKeditor} this
26492          * @param {Object} the FCK object.
26493          */
26494         editorinit : true
26495     });
26496     
26497     
26498 };
26499 Roo.form.FCKeditor.editors = { };
26500 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
26501 {
26502     //defaultAutoCreate : {
26503     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
26504     //},
26505     // private
26506     /**
26507      * @cfg {Object} fck options - see fck manual for details.
26508      */
26509     fckconfig : false,
26510     
26511     /**
26512      * @cfg {Object} fck toolbar set (Basic or Default)
26513      */
26514     toolbarSet : 'Basic',
26515     /**
26516      * @cfg {Object} fck BasePath
26517      */ 
26518     basePath : '/fckeditor/',
26519     
26520     
26521     frame : false,
26522     
26523     value : '',
26524     
26525    
26526     onRender : function(ct, position)
26527     {
26528         if(!this.el){
26529             this.defaultAutoCreate = {
26530                 tag: "textarea",
26531                 style:"width:300px;height:60px;",
26532                 autocomplete: "new-password"
26533             };
26534         }
26535         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
26536         /*
26537         if(this.grow){
26538             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
26539             if(this.preventScrollbars){
26540                 this.el.setStyle("overflow", "hidden");
26541             }
26542             this.el.setHeight(this.growMin);
26543         }
26544         */
26545         //console.log('onrender' + this.getId() );
26546         Roo.form.FCKeditor.editors[this.getId()] = this;
26547          
26548
26549         this.replaceTextarea() ;
26550         
26551     },
26552     
26553     getEditor : function() {
26554         return this.fckEditor;
26555     },
26556     /**
26557      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
26558      * @param {Mixed} value The value to set
26559      */
26560     
26561     
26562     setValue : function(value)
26563     {
26564         //console.log('setValue: ' + value);
26565         
26566         if(typeof(value) == 'undefined') { // not sure why this is happending...
26567             return;
26568         }
26569         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26570         
26571         //if(!this.el || !this.getEditor()) {
26572         //    this.value = value;
26573             //this.setValue.defer(100,this,[value]);    
26574         //    return;
26575         //} 
26576         
26577         if(!this.getEditor()) {
26578             return;
26579         }
26580         
26581         this.getEditor().SetData(value);
26582         
26583         //
26584
26585     },
26586
26587     /**
26588      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
26589      * @return {Mixed} value The field value
26590      */
26591     getValue : function()
26592     {
26593         
26594         if (this.frame && this.frame.dom.style.display == 'none') {
26595             return Roo.form.FCKeditor.superclass.getValue.call(this);
26596         }
26597         
26598         if(!this.el || !this.getEditor()) {
26599            
26600            // this.getValue.defer(100,this); 
26601             return this.value;
26602         }
26603        
26604         
26605         var value=this.getEditor().GetData();
26606         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
26607         return Roo.form.FCKeditor.superclass.getValue.call(this);
26608         
26609
26610     },
26611
26612     /**
26613      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
26614      * @return {Mixed} value The field value
26615      */
26616     getRawValue : function()
26617     {
26618         if (this.frame && this.frame.dom.style.display == 'none') {
26619             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26620         }
26621         
26622         if(!this.el || !this.getEditor()) {
26623             //this.getRawValue.defer(100,this); 
26624             return this.value;
26625             return;
26626         }
26627         
26628         
26629         
26630         var value=this.getEditor().GetData();
26631         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
26632         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
26633          
26634     },
26635     
26636     setSize : function(w,h) {
26637         
26638         
26639         
26640         //if (this.frame && this.frame.dom.style.display == 'none') {
26641         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26642         //    return;
26643         //}
26644         //if(!this.el || !this.getEditor()) {
26645         //    this.setSize.defer(100,this, [w,h]); 
26646         //    return;
26647         //}
26648         
26649         
26650         
26651         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
26652         
26653         this.frame.dom.setAttribute('width', w);
26654         this.frame.dom.setAttribute('height', h);
26655         this.frame.setSize(w,h);
26656         
26657     },
26658     
26659     toggleSourceEdit : function(value) {
26660         
26661       
26662          
26663         this.el.dom.style.display = value ? '' : 'none';
26664         this.frame.dom.style.display = value ?  'none' : '';
26665         
26666     },
26667     
26668     
26669     focus: function(tag)
26670     {
26671         if (this.frame.dom.style.display == 'none') {
26672             return Roo.form.FCKeditor.superclass.focus.call(this);
26673         }
26674         if(!this.el || !this.getEditor()) {
26675             this.focus.defer(100,this, [tag]); 
26676             return;
26677         }
26678         
26679         
26680         
26681         
26682         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
26683         this.getEditor().Focus();
26684         if (tgs.length) {
26685             if (!this.getEditor().Selection.GetSelection()) {
26686                 this.focus.defer(100,this, [tag]); 
26687                 return;
26688             }
26689             
26690             
26691             var r = this.getEditor().EditorDocument.createRange();
26692             r.setStart(tgs[0],0);
26693             r.setEnd(tgs[0],0);
26694             this.getEditor().Selection.GetSelection().removeAllRanges();
26695             this.getEditor().Selection.GetSelection().addRange(r);
26696             this.getEditor().Focus();
26697         }
26698         
26699     },
26700     
26701     
26702     
26703     replaceTextarea : function()
26704     {
26705         if ( document.getElementById( this.getId() + '___Frame' ) ) {
26706             return ;
26707         }
26708         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
26709         //{
26710             // We must check the elements firstly using the Id and then the name.
26711         var oTextarea = document.getElementById( this.getId() );
26712         
26713         var colElementsByName = document.getElementsByName( this.getId() ) ;
26714          
26715         oTextarea.style.display = 'none' ;
26716
26717         if ( oTextarea.tabIndex ) {            
26718             this.TabIndex = oTextarea.tabIndex ;
26719         }
26720         
26721         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
26722         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
26723         this.frame = Roo.get(this.getId() + '___Frame')
26724     },
26725     
26726     _getConfigHtml : function()
26727     {
26728         var sConfig = '' ;
26729
26730         for ( var o in this.fckconfig ) {
26731             sConfig += sConfig.length > 0  ? '&amp;' : '';
26732             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
26733         }
26734
26735         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
26736     },
26737     
26738     
26739     _getIFrameHtml : function()
26740     {
26741         var sFile = 'fckeditor.html' ;
26742         /* no idea what this is about..
26743         try
26744         {
26745             if ( (/fcksource=true/i).test( window.top.location.search ) )
26746                 sFile = 'fckeditor.original.html' ;
26747         }
26748         catch (e) { 
26749         */
26750
26751         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
26752         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
26753         
26754         
26755         var html = '<iframe id="' + this.getId() +
26756             '___Frame" src="' + sLink +
26757             '" width="' + this.width +
26758             '" height="' + this.height + '"' +
26759             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
26760             ' frameborder="0" scrolling="no"></iframe>' ;
26761
26762         return html ;
26763     },
26764     
26765     _insertHtmlBefore : function( html, element )
26766     {
26767         if ( element.insertAdjacentHTML )       {
26768             // IE
26769             element.insertAdjacentHTML( 'beforeBegin', html ) ;
26770         } else { // Gecko
26771             var oRange = document.createRange() ;
26772             oRange.setStartBefore( element ) ;
26773             var oFragment = oRange.createContextualFragment( html );
26774             element.parentNode.insertBefore( oFragment, element ) ;
26775         }
26776     }
26777     
26778     
26779   
26780     
26781     
26782     
26783     
26784
26785 });
26786
26787 //Roo.reg('fckeditor', Roo.form.FCKeditor);
26788
26789 function FCKeditor_OnComplete(editorInstance){
26790     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
26791     f.fckEditor = editorInstance;
26792     //console.log("loaded");
26793     f.fireEvent('editorinit', f, editorInstance);
26794
26795   
26796
26797  
26798
26799
26800
26801
26802
26803
26804
26805
26806
26807
26808
26809
26810
26811
26812
26813 //<script type="text/javascript">
26814 /**
26815  * @class Roo.form.GridField
26816  * @extends Roo.form.Field
26817  * Embed a grid (or editable grid into a form)
26818  * STATUS ALPHA
26819  * 
26820  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
26821  * it needs 
26822  * xgrid.store = Roo.data.Store
26823  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
26824  * xgrid.store.reader = Roo.data.JsonReader 
26825  * 
26826  * 
26827  * @constructor
26828  * Creates a new GridField
26829  * @param {Object} config Configuration options
26830  */
26831 Roo.form.GridField = function(config){
26832     Roo.form.GridField.superclass.constructor.call(this, config);
26833      
26834 };
26835
26836 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
26837     /**
26838      * @cfg {Number} width  - used to restrict width of grid..
26839      */
26840     width : 100,
26841     /**
26842      * @cfg {Number} height - used to restrict height of grid..
26843      */
26844     height : 50,
26845      /**
26846      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
26847          * 
26848          *}
26849      */
26850     xgrid : false, 
26851     /**
26852      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
26853      * {tag: "input", type: "checkbox", autocomplete: "off"})
26854      */
26855    // defaultAutoCreate : { tag: 'div' },
26856     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
26857     /**
26858      * @cfg {String} addTitle Text to include for adding a title.
26859      */
26860     addTitle : false,
26861     //
26862     onResize : function(){
26863         Roo.form.Field.superclass.onResize.apply(this, arguments);
26864     },
26865
26866     initEvents : function(){
26867         // Roo.form.Checkbox.superclass.initEvents.call(this);
26868         // has no events...
26869        
26870     },
26871
26872
26873     getResizeEl : function(){
26874         return this.wrap;
26875     },
26876
26877     getPositionEl : function(){
26878         return this.wrap;
26879     },
26880
26881     // private
26882     onRender : function(ct, position){
26883         
26884         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
26885         var style = this.style;
26886         delete this.style;
26887         
26888         Roo.form.GridField.superclass.onRender.call(this, ct, position);
26889         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
26890         this.viewEl = this.wrap.createChild({ tag: 'div' });
26891         if (style) {
26892             this.viewEl.applyStyles(style);
26893         }
26894         if (this.width) {
26895             this.viewEl.setWidth(this.width);
26896         }
26897         if (this.height) {
26898             this.viewEl.setHeight(this.height);
26899         }
26900         //if(this.inputValue !== undefined){
26901         //this.setValue(this.value);
26902         
26903         
26904         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
26905         
26906         
26907         this.grid.render();
26908         this.grid.getDataSource().on('remove', this.refreshValue, this);
26909         this.grid.getDataSource().on('update', this.refreshValue, this);
26910         this.grid.on('afteredit', this.refreshValue, this);
26911  
26912     },
26913      
26914     
26915     /**
26916      * Sets the value of the item. 
26917      * @param {String} either an object  or a string..
26918      */
26919     setValue : function(v){
26920         //this.value = v;
26921         v = v || []; // empty set..
26922         // this does not seem smart - it really only affects memoryproxy grids..
26923         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
26924             var ds = this.grid.getDataSource();
26925             // assumes a json reader..
26926             var data = {}
26927             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
26928             ds.loadData( data);
26929         }
26930         // clear selection so it does not get stale.
26931         if (this.grid.sm) { 
26932             this.grid.sm.clearSelections();
26933         }
26934         
26935         Roo.form.GridField.superclass.setValue.call(this, v);
26936         this.refreshValue();
26937         // should load data in the grid really....
26938     },
26939     
26940     // private
26941     refreshValue: function() {
26942          var val = [];
26943         this.grid.getDataSource().each(function(r) {
26944             val.push(r.data);
26945         });
26946         this.el.dom.value = Roo.encode(val);
26947     }
26948     
26949      
26950     
26951     
26952 });/*
26953  * Based on:
26954  * Ext JS Library 1.1.1
26955  * Copyright(c) 2006-2007, Ext JS, LLC.
26956  *
26957  * Originally Released Under LGPL - original licence link has changed is not relivant.
26958  *
26959  * Fork - LGPL
26960  * <script type="text/javascript">
26961  */
26962 /**
26963  * @class Roo.form.DisplayField
26964  * @extends Roo.form.Field
26965  * A generic Field to display non-editable data.
26966  * @cfg {Boolean} closable (true|false) default false
26967  * @constructor
26968  * Creates a new Display Field item.
26969  * @param {Object} config Configuration options
26970  */
26971 Roo.form.DisplayField = function(config){
26972     Roo.form.DisplayField.superclass.constructor.call(this, config);
26973     
26974     this.addEvents({
26975         /**
26976          * @event close
26977          * Fires after the click the close btn
26978              * @param {Roo.form.DisplayField} this
26979              */
26980         close : true
26981     });
26982 };
26983
26984 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
26985     inputType:      'hidden',
26986     allowBlank:     true,
26987     readOnly:         true,
26988     
26989  
26990     /**
26991      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
26992      */
26993     focusClass : undefined,
26994     /**
26995      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
26996      */
26997     fieldClass: 'x-form-field',
26998     
26999      /**
27000      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
27001      */
27002     valueRenderer: undefined,
27003     
27004     width: 100,
27005     /**
27006      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27007      * {tag: "input", type: "checkbox", autocomplete: "off"})
27008      */
27009      
27010  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
27011  
27012     closable : false,
27013     
27014     onResize : function(){
27015         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
27016         
27017     },
27018
27019     initEvents : function(){
27020         // Roo.form.Checkbox.superclass.initEvents.call(this);
27021         // has no events...
27022         
27023         if(this.closable){
27024             this.closeEl.on('click', this.onClose, this);
27025         }
27026        
27027     },
27028
27029
27030     getResizeEl : function(){
27031         return this.wrap;
27032     },
27033
27034     getPositionEl : function(){
27035         return this.wrap;
27036     },
27037
27038     // private
27039     onRender : function(ct, position){
27040         
27041         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
27042         //if(this.inputValue !== undefined){
27043         this.wrap = this.el.wrap();
27044         
27045         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
27046         
27047         if(this.closable){
27048             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
27049         }
27050         
27051         if (this.bodyStyle) {
27052             this.viewEl.applyStyles(this.bodyStyle);
27053         }
27054         //this.viewEl.setStyle('padding', '2px');
27055         
27056         this.setValue(this.value);
27057         
27058     },
27059 /*
27060     // private
27061     initValue : Roo.emptyFn,
27062
27063   */
27064
27065         // private
27066     onClick : function(){
27067         
27068     },
27069
27070     /**
27071      * Sets the checked state of the checkbox.
27072      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
27073      */
27074     setValue : function(v){
27075         this.value = v;
27076         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
27077         // this might be called before we have a dom element..
27078         if (!this.viewEl) {
27079             return;
27080         }
27081         this.viewEl.dom.innerHTML = html;
27082         Roo.form.DisplayField.superclass.setValue.call(this, v);
27083
27084     },
27085     
27086     onClose : function(e)
27087     {
27088         e.preventDefault();
27089         
27090         this.fireEvent('close', this);
27091     }
27092 });/*
27093  * 
27094  * Licence- LGPL
27095  * 
27096  */
27097
27098 /**
27099  * @class Roo.form.DayPicker
27100  * @extends Roo.form.Field
27101  * A Day picker show [M] [T] [W] ....
27102  * @constructor
27103  * Creates a new Day Picker
27104  * @param {Object} config Configuration options
27105  */
27106 Roo.form.DayPicker= function(config){
27107     Roo.form.DayPicker.superclass.constructor.call(this, config);
27108      
27109 };
27110
27111 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
27112     /**
27113      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
27114      */
27115     focusClass : undefined,
27116     /**
27117      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
27118      */
27119     fieldClass: "x-form-field",
27120    
27121     /**
27122      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
27123      * {tag: "input", type: "checkbox", autocomplete: "off"})
27124      */
27125     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
27126     
27127    
27128     actionMode : 'viewEl', 
27129     //
27130     // private
27131  
27132     inputType : 'hidden',
27133     
27134      
27135     inputElement: false, // real input element?
27136     basedOn: false, // ????
27137     
27138     isFormField: true, // not sure where this is needed!!!!
27139
27140     onResize : function(){
27141         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
27142         if(!this.boxLabel){
27143             this.el.alignTo(this.wrap, 'c-c');
27144         }
27145     },
27146
27147     initEvents : function(){
27148         Roo.form.Checkbox.superclass.initEvents.call(this);
27149         this.el.on("click", this.onClick,  this);
27150         this.el.on("change", this.onClick,  this);
27151     },
27152
27153
27154     getResizeEl : function(){
27155         return this.wrap;
27156     },
27157
27158     getPositionEl : function(){
27159         return this.wrap;
27160     },
27161
27162     
27163     // private
27164     onRender : function(ct, position){
27165         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
27166        
27167         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
27168         
27169         var r1 = '<table><tr>';
27170         var r2 = '<tr class="x-form-daypick-icons">';
27171         for (var i=0; i < 7; i++) {
27172             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
27173             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
27174         }
27175         
27176         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
27177         viewEl.select('img').on('click', this.onClick, this);
27178         this.viewEl = viewEl;   
27179         
27180         
27181         // this will not work on Chrome!!!
27182         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
27183         this.el.on('propertychange', this.setFromHidden,  this);  //ie
27184         
27185         
27186           
27187
27188     },
27189
27190     // private
27191     initValue : Roo.emptyFn,
27192
27193     /**
27194      * Returns the checked state of the checkbox.
27195      * @return {Boolean} True if checked, else false
27196      */
27197     getValue : function(){
27198         return this.el.dom.value;
27199         
27200     },
27201
27202         // private
27203     onClick : function(e){ 
27204         //this.setChecked(!this.checked);
27205         Roo.get(e.target).toggleClass('x-menu-item-checked');
27206         this.refreshValue();
27207         //if(this.el.dom.checked != this.checked){
27208         //    this.setValue(this.el.dom.checked);
27209        // }
27210     },
27211     
27212     // private
27213     refreshValue : function()
27214     {
27215         var val = '';
27216         this.viewEl.select('img',true).each(function(e,i,n)  {
27217             val += e.is(".x-menu-item-checked") ? String(n) : '';
27218         });
27219         this.setValue(val, true);
27220     },
27221
27222     /**
27223      * Sets the checked state of the checkbox.
27224      * On is always based on a string comparison between inputValue and the param.
27225      * @param {Boolean/String} value - the value to set 
27226      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
27227      */
27228     setValue : function(v,suppressEvent){
27229         if (!this.el.dom) {
27230             return;
27231         }
27232         var old = this.el.dom.value ;
27233         this.el.dom.value = v;
27234         if (suppressEvent) {
27235             return ;
27236         }
27237          
27238         // update display..
27239         this.viewEl.select('img',true).each(function(e,i,n)  {
27240             
27241             var on = e.is(".x-menu-item-checked");
27242             var newv = v.indexOf(String(n)) > -1;
27243             if (on != newv) {
27244                 e.toggleClass('x-menu-item-checked');
27245             }
27246             
27247         });
27248         
27249         
27250         this.fireEvent('change', this, v, old);
27251         
27252         
27253     },
27254    
27255     // handle setting of hidden value by some other method!!?!?
27256     setFromHidden: function()
27257     {
27258         if(!this.el){
27259             return;
27260         }
27261         //console.log("SET FROM HIDDEN");
27262         //alert('setFrom hidden');
27263         this.setValue(this.el.dom.value);
27264     },
27265     
27266     onDestroy : function()
27267     {
27268         if(this.viewEl){
27269             Roo.get(this.viewEl).remove();
27270         }
27271          
27272         Roo.form.DayPicker.superclass.onDestroy.call(this);
27273     }
27274
27275 });/*
27276  * RooJS Library 1.1.1
27277  * Copyright(c) 2008-2011  Alan Knowles
27278  *
27279  * License - LGPL
27280  */
27281  
27282
27283 /**
27284  * @class Roo.form.ComboCheck
27285  * @extends Roo.form.ComboBox
27286  * A combobox for multiple select items.
27287  *
27288  * FIXME - could do with a reset button..
27289  * 
27290  * @constructor
27291  * Create a new ComboCheck
27292  * @param {Object} config Configuration options
27293  */
27294 Roo.form.ComboCheck = function(config){
27295     Roo.form.ComboCheck.superclass.constructor.call(this, config);
27296     // should verify some data...
27297     // like
27298     // hiddenName = required..
27299     // displayField = required
27300     // valudField == required
27301     var req= [ 'hiddenName', 'displayField', 'valueField' ];
27302     var _t = this;
27303     Roo.each(req, function(e) {
27304         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
27305             throw "Roo.form.ComboCheck : missing value for: " + e;
27306         }
27307     });
27308     
27309     
27310 };
27311
27312 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
27313      
27314      
27315     editable : false,
27316      
27317     selectedClass: 'x-menu-item-checked', 
27318     
27319     // private
27320     onRender : function(ct, position){
27321         var _t = this;
27322         
27323         
27324         
27325         if(!this.tpl){
27326             var cls = 'x-combo-list';
27327
27328             
27329             this.tpl =  new Roo.Template({
27330                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
27331                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
27332                    '<span>{' + this.displayField + '}</span>' +
27333                     '</div>' 
27334                 
27335             });
27336         }
27337  
27338         
27339         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
27340         this.view.singleSelect = false;
27341         this.view.multiSelect = true;
27342         this.view.toggleSelect = true;
27343         this.pageTb.add(new Roo.Toolbar.Fill(), {
27344             
27345             text: 'Done',
27346             handler: function()
27347             {
27348                 _t.collapse();
27349             }
27350         });
27351     },
27352     
27353     onViewOver : function(e, t){
27354         // do nothing...
27355         return;
27356         
27357     },
27358     
27359     onViewClick : function(doFocus,index){
27360         return;
27361         
27362     },
27363     select: function () {
27364         //Roo.log("SELECT CALLED");
27365     },
27366      
27367     selectByValue : function(xv, scrollIntoView){
27368         var ar = this.getValueArray();
27369         var sels = [];
27370         
27371         Roo.each(ar, function(v) {
27372             if(v === undefined || v === null){
27373                 return;
27374             }
27375             var r = this.findRecord(this.valueField, v);
27376             if(r){
27377                 sels.push(this.store.indexOf(r))
27378                 
27379             }
27380         },this);
27381         this.view.select(sels);
27382         return false;
27383     },
27384     
27385     
27386     
27387     onSelect : function(record, index){
27388        // Roo.log("onselect Called");
27389        // this is only called by the clear button now..
27390         this.view.clearSelections();
27391         this.setValue('[]');
27392         if (this.value != this.valueBefore) {
27393             this.fireEvent('change', this, this.value, this.valueBefore);
27394             this.valueBefore = this.value;
27395         }
27396     },
27397     getValueArray : function()
27398     {
27399         var ar = [] ;
27400         
27401         try {
27402             //Roo.log(this.value);
27403             if (typeof(this.value) == 'undefined') {
27404                 return [];
27405             }
27406             var ar = Roo.decode(this.value);
27407             return  ar instanceof Array ? ar : []; //?? valid?
27408             
27409         } catch(e) {
27410             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
27411             return [];
27412         }
27413          
27414     },
27415     expand : function ()
27416     {
27417         
27418         Roo.form.ComboCheck.superclass.expand.call(this);
27419         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
27420         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
27421         
27422
27423     },
27424     
27425     collapse : function(){
27426         Roo.form.ComboCheck.superclass.collapse.call(this);
27427         var sl = this.view.getSelectedIndexes();
27428         var st = this.store;
27429         var nv = [];
27430         var tv = [];
27431         var r;
27432         Roo.each(sl, function(i) {
27433             r = st.getAt(i);
27434             nv.push(r.get(this.valueField));
27435         },this);
27436         this.setValue(Roo.encode(nv));
27437         if (this.value != this.valueBefore) {
27438
27439             this.fireEvent('change', this, this.value, this.valueBefore);
27440             this.valueBefore = this.value;
27441         }
27442         
27443     },
27444     
27445     setValue : function(v){
27446         // Roo.log(v);
27447         this.value = v;
27448         
27449         var vals = this.getValueArray();
27450         var tv = [];
27451         Roo.each(vals, function(k) {
27452             var r = this.findRecord(this.valueField, k);
27453             if(r){
27454                 tv.push(r.data[this.displayField]);
27455             }else if(this.valueNotFoundText !== undefined){
27456                 tv.push( this.valueNotFoundText );
27457             }
27458         },this);
27459        // Roo.log(tv);
27460         
27461         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
27462         this.hiddenField.value = v;
27463         this.value = v;
27464     }
27465     
27466 });/*
27467  * Based on:
27468  * Ext JS Library 1.1.1
27469  * Copyright(c) 2006-2007, Ext JS, LLC.
27470  *
27471  * Originally Released Under LGPL - original licence link has changed is not relivant.
27472  *
27473  * Fork - LGPL
27474  * <script type="text/javascript">
27475  */
27476  
27477 /**
27478  * @class Roo.form.Signature
27479  * @extends Roo.form.Field
27480  * Signature field.  
27481  * @constructor
27482  * 
27483  * @param {Object} config Configuration options
27484  */
27485
27486 Roo.form.Signature = function(config){
27487     Roo.form.Signature.superclass.constructor.call(this, config);
27488     
27489     this.addEvents({// not in used??
27490          /**
27491          * @event confirm
27492          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
27493              * @param {Roo.form.Signature} combo This combo box
27494              */
27495         'confirm' : true,
27496         /**
27497          * @event reset
27498          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
27499              * @param {Roo.form.ComboBox} combo This combo box
27500              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
27501              */
27502         'reset' : true
27503     });
27504 };
27505
27506 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
27507     /**
27508      * @cfg {Object} labels Label to use when rendering a form.
27509      * defaults to 
27510      * labels : { 
27511      *      clear : "Clear",
27512      *      confirm : "Confirm"
27513      *  }
27514      */
27515     labels : { 
27516         clear : "Clear",
27517         confirm : "Confirm"
27518     },
27519     /**
27520      * @cfg {Number} width The signature panel width (defaults to 300)
27521      */
27522     width: 300,
27523     /**
27524      * @cfg {Number} height The signature panel height (defaults to 100)
27525      */
27526     height : 100,
27527     /**
27528      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
27529      */
27530     allowBlank : false,
27531     
27532     //private
27533     // {Object} signPanel The signature SVG panel element (defaults to {})
27534     signPanel : {},
27535     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
27536     isMouseDown : false,
27537     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
27538     isConfirmed : false,
27539     // {String} signatureTmp SVG mapping string (defaults to empty string)
27540     signatureTmp : '',
27541     
27542     
27543     defaultAutoCreate : { // modified by initCompnoent..
27544         tag: "input",
27545         type:"hidden"
27546     },
27547
27548     // private
27549     onRender : function(ct, position){
27550         
27551         Roo.form.Signature.superclass.onRender.call(this, ct, position);
27552         
27553         this.wrap = this.el.wrap({
27554             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
27555         });
27556         
27557         this.createToolbar(this);
27558         this.signPanel = this.wrap.createChild({
27559                 tag: 'div',
27560                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
27561             }, this.el
27562         );
27563             
27564         this.svgID = Roo.id();
27565         this.svgEl = this.signPanel.createChild({
27566               xmlns : 'http://www.w3.org/2000/svg',
27567               tag : 'svg',
27568               id : this.svgID + "-svg",
27569               width: this.width,
27570               height: this.height,
27571               viewBox: '0 0 '+this.width+' '+this.height,
27572               cn : [
27573                 {
27574                     tag: "rect",
27575                     id: this.svgID + "-svg-r",
27576                     width: this.width,
27577                     height: this.height,
27578                     fill: "#ffa"
27579                 },
27580                 {
27581                     tag: "line",
27582                     id: this.svgID + "-svg-l",
27583                     x1: "0", // start
27584                     y1: (this.height*0.8), // start set the line in 80% of height
27585                     x2: this.width, // end
27586                     y2: (this.height*0.8), // end set the line in 80% of height
27587                     'stroke': "#666",
27588                     'stroke-width': "1",
27589                     'stroke-dasharray': "3",
27590                     'shape-rendering': "crispEdges",
27591                     'pointer-events': "none"
27592                 },
27593                 {
27594                     tag: "path",
27595                     id: this.svgID + "-svg-p",
27596                     'stroke': "navy",
27597                     'stroke-width': "3",
27598                     'fill': "none",
27599                     'pointer-events': 'none'
27600                 }
27601               ]
27602         });
27603         this.createSVG();
27604         this.svgBox = this.svgEl.dom.getScreenCTM();
27605     },
27606     createSVG : function(){ 
27607         var svg = this.signPanel;
27608         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
27609         var t = this;
27610
27611         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
27612         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
27613         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
27614         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
27615         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
27616         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
27617         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
27618         
27619     },
27620     isTouchEvent : function(e){
27621         return e.type.match(/^touch/);
27622     },
27623     getCoords : function (e) {
27624         var pt    = this.svgEl.dom.createSVGPoint();
27625         pt.x = e.clientX; 
27626         pt.y = e.clientY;
27627         if (this.isTouchEvent(e)) {
27628             pt.x =  e.targetTouches[0].clientX;
27629             pt.y = e.targetTouches[0].clientY;
27630         }
27631         var a = this.svgEl.dom.getScreenCTM();
27632         var b = a.inverse();
27633         var mx = pt.matrixTransform(b);
27634         return mx.x + ',' + mx.y;
27635     },
27636     //mouse event headler 
27637     down : function (e) {
27638         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
27639         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
27640         
27641         this.isMouseDown = true;
27642         
27643         e.preventDefault();
27644     },
27645     move : function (e) {
27646         if (this.isMouseDown) {
27647             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
27648             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
27649         }
27650         
27651         e.preventDefault();
27652     },
27653     up : function (e) {
27654         this.isMouseDown = false;
27655         var sp = this.signatureTmp.split(' ');
27656         
27657         if(sp.length > 1){
27658             if(!sp[sp.length-2].match(/^L/)){
27659                 sp.pop();
27660                 sp.pop();
27661                 sp.push("");
27662                 this.signatureTmp = sp.join(" ");
27663             }
27664         }
27665         if(this.getValue() != this.signatureTmp){
27666             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27667             this.isConfirmed = false;
27668         }
27669         e.preventDefault();
27670     },
27671     
27672     /**
27673      * Protected method that will not generally be called directly. It
27674      * is called when the editor creates its toolbar. Override this method if you need to
27675      * add custom toolbar buttons.
27676      * @param {HtmlEditor} editor
27677      */
27678     createToolbar : function(editor){
27679          function btn(id, toggle, handler){
27680             var xid = fid + '-'+ id ;
27681             return {
27682                 id : xid,
27683                 cmd : id,
27684                 cls : 'x-btn-icon x-edit-'+id,
27685                 enableToggle:toggle !== false,
27686                 scope: editor, // was editor...
27687                 handler:handler||editor.relayBtnCmd,
27688                 clickEvent:'mousedown',
27689                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
27690                 tabIndex:-1
27691             };
27692         }
27693         
27694         
27695         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
27696         this.tb = tb;
27697         this.tb.add(
27698            {
27699                 cls : ' x-signature-btn x-signature-'+id,
27700                 scope: editor, // was editor...
27701                 handler: this.reset,
27702                 clickEvent:'mousedown',
27703                 text: this.labels.clear
27704             },
27705             {
27706                  xtype : 'Fill',
27707                  xns: Roo.Toolbar
27708             }, 
27709             {
27710                 cls : '  x-signature-btn x-signature-'+id,
27711                 scope: editor, // was editor...
27712                 handler: this.confirmHandler,
27713                 clickEvent:'mousedown',
27714                 text: this.labels.confirm
27715             }
27716         );
27717     
27718     },
27719     //public
27720     /**
27721      * when user is clicked confirm then show this image.....
27722      * 
27723      * @return {String} Image Data URI
27724      */
27725     getImageDataURI : function(){
27726         var svg = this.svgEl.dom.parentNode.innerHTML;
27727         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
27728         return src; 
27729     },
27730     /**
27731      * 
27732      * @return {Boolean} this.isConfirmed
27733      */
27734     getConfirmed : function(){
27735         return this.isConfirmed;
27736     },
27737     /**
27738      * 
27739      * @return {Number} this.width
27740      */
27741     getWidth : function(){
27742         return this.width;
27743     },
27744     /**
27745      * 
27746      * @return {Number} this.height
27747      */
27748     getHeight : function(){
27749         return this.height;
27750     },
27751     // private
27752     getSignature : function(){
27753         return this.signatureTmp;
27754     },
27755     // private
27756     reset : function(){
27757         this.signatureTmp = '';
27758         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27759         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
27760         this.isConfirmed = false;
27761         Roo.form.Signature.superclass.reset.call(this);
27762     },
27763     setSignature : function(s){
27764         this.signatureTmp = s;
27765         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
27766         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
27767         this.setValue(s);
27768         this.isConfirmed = false;
27769         Roo.form.Signature.superclass.reset.call(this);
27770     }, 
27771     test : function(){
27772 //        Roo.log(this.signPanel.dom.contentWindow.up())
27773     },
27774     //private
27775     setConfirmed : function(){
27776         
27777         
27778         
27779 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
27780     },
27781     // private
27782     confirmHandler : function(){
27783         if(!this.getSignature()){
27784             return;
27785         }
27786         
27787         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
27788         this.setValue(this.getSignature());
27789         this.isConfirmed = true;
27790         
27791         this.fireEvent('confirm', this);
27792     },
27793     // private
27794     // Subclasses should provide the validation implementation by overriding this
27795     validateValue : function(value){
27796         if(this.allowBlank){
27797             return true;
27798         }
27799         
27800         if(this.isConfirmed){
27801             return true;
27802         }
27803         return false;
27804     }
27805 });/*
27806  * Based on:
27807  * Ext JS Library 1.1.1
27808  * Copyright(c) 2006-2007, Ext JS, LLC.
27809  *
27810  * Originally Released Under LGPL - original licence link has changed is not relivant.
27811  *
27812  * Fork - LGPL
27813  * <script type="text/javascript">
27814  */
27815  
27816
27817 /**
27818  * @class Roo.form.ComboBox
27819  * @extends Roo.form.TriggerField
27820  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
27821  * @constructor
27822  * Create a new ComboBox.
27823  * @param {Object} config Configuration options
27824  */
27825 Roo.form.Select = function(config){
27826     Roo.form.Select.superclass.constructor.call(this, config);
27827      
27828 };
27829
27830 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
27831     /**
27832      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
27833      */
27834     /**
27835      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
27836      * rendering into an Roo.Editor, defaults to false)
27837      */
27838     /**
27839      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
27840      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
27841      */
27842     /**
27843      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
27844      */
27845     /**
27846      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
27847      * the dropdown list (defaults to undefined, with no header element)
27848      */
27849
27850      /**
27851      * @cfg {String/Roo.Template} tpl The template to use to render the output
27852      */
27853      
27854     // private
27855     defaultAutoCreate : {tag: "select"  },
27856     /**
27857      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
27858      */
27859     listWidth: undefined,
27860     /**
27861      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
27862      * mode = 'remote' or 'text' if mode = 'local')
27863      */
27864     displayField: undefined,
27865     /**
27866      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
27867      * mode = 'remote' or 'value' if mode = 'local'). 
27868      * Note: use of a valueField requires the user make a selection
27869      * in order for a value to be mapped.
27870      */
27871     valueField: undefined,
27872     
27873     
27874     /**
27875      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
27876      * field's data value (defaults to the underlying DOM element's name)
27877      */
27878     hiddenName: undefined,
27879     /**
27880      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
27881      */
27882     listClass: '',
27883     /**
27884      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
27885      */
27886     selectedClass: 'x-combo-selected',
27887     /**
27888      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
27889      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
27890      * which displays a downward arrow icon).
27891      */
27892     triggerClass : 'x-form-arrow-trigger',
27893     /**
27894      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
27895      */
27896     shadow:'sides',
27897     /**
27898      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
27899      * anchor positions (defaults to 'tl-bl')
27900      */
27901     listAlign: 'tl-bl?',
27902     /**
27903      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
27904      */
27905     maxHeight: 300,
27906     /**
27907      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
27908      * query specified by the allQuery config option (defaults to 'query')
27909      */
27910     triggerAction: 'query',
27911     /**
27912      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
27913      * (defaults to 4, does not apply if editable = false)
27914      */
27915     minChars : 4,
27916     /**
27917      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
27918      * delay (typeAheadDelay) if it matches a known value (defaults to false)
27919      */
27920     typeAhead: false,
27921     /**
27922      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
27923      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
27924      */
27925     queryDelay: 500,
27926     /**
27927      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
27928      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
27929      */
27930     pageSize: 0,
27931     /**
27932      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
27933      * when editable = true (defaults to false)
27934      */
27935     selectOnFocus:false,
27936     /**
27937      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
27938      */
27939     queryParam: 'query',
27940     /**
27941      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
27942      * when mode = 'remote' (defaults to 'Loading...')
27943      */
27944     loadingText: 'Loading...',
27945     /**
27946      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
27947      */
27948     resizable: false,
27949     /**
27950      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
27951      */
27952     handleHeight : 8,
27953     /**
27954      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
27955      * traditional select (defaults to true)
27956      */
27957     editable: true,
27958     /**
27959      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
27960      */
27961     allQuery: '',
27962     /**
27963      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
27964      */
27965     mode: 'remote',
27966     /**
27967      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
27968      * listWidth has a higher value)
27969      */
27970     minListWidth : 70,
27971     /**
27972      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
27973      * allow the user to set arbitrary text into the field (defaults to false)
27974      */
27975     forceSelection:false,
27976     /**
27977      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
27978      * if typeAhead = true (defaults to 250)
27979      */
27980     typeAheadDelay : 250,
27981     /**
27982      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
27983      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
27984      */
27985     valueNotFoundText : undefined,
27986     
27987     /**
27988      * @cfg {String} defaultValue The value displayed after loading the store.
27989      */
27990     defaultValue: '',
27991     
27992     /**
27993      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
27994      */
27995     blockFocus : false,
27996     
27997     /**
27998      * @cfg {Boolean} disableClear Disable showing of clear button.
27999      */
28000     disableClear : false,
28001     /**
28002      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
28003      */
28004     alwaysQuery : false,
28005     
28006     //private
28007     addicon : false,
28008     editicon: false,
28009     
28010     // element that contains real text value.. (when hidden is used..)
28011      
28012     // private
28013     onRender : function(ct, position){
28014         Roo.form.Field.prototype.onRender.call(this, ct, position);
28015         
28016         if(this.store){
28017             this.store.on('beforeload', this.onBeforeLoad, this);
28018             this.store.on('load', this.onLoad, this);
28019             this.store.on('loadexception', this.onLoadException, this);
28020             this.store.load({});
28021         }
28022         
28023         
28024         
28025     },
28026
28027     // private
28028     initEvents : function(){
28029         //Roo.form.ComboBox.superclass.initEvents.call(this);
28030  
28031     },
28032
28033     onDestroy : function(){
28034        
28035         if(this.store){
28036             this.store.un('beforeload', this.onBeforeLoad, this);
28037             this.store.un('load', this.onLoad, this);
28038             this.store.un('loadexception', this.onLoadException, this);
28039         }
28040         //Roo.form.ComboBox.superclass.onDestroy.call(this);
28041     },
28042
28043     // private
28044     fireKey : function(e){
28045         if(e.isNavKeyPress() && !this.list.isVisible()){
28046             this.fireEvent("specialkey", this, e);
28047         }
28048     },
28049
28050     // private
28051     onResize: function(w, h){
28052         
28053         return; 
28054     
28055         
28056     },
28057
28058     /**
28059      * Allow or prevent the user from directly editing the field text.  If false is passed,
28060      * the user will only be able to select from the items defined in the dropdown list.  This method
28061      * is the runtime equivalent of setting the 'editable' config option at config time.
28062      * @param {Boolean} value True to allow the user to directly edit the field text
28063      */
28064     setEditable : function(value){
28065          
28066     },
28067
28068     // private
28069     onBeforeLoad : function(){
28070         
28071         Roo.log("Select before load");
28072         return;
28073     
28074         this.innerList.update(this.loadingText ?
28075                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
28076         //this.restrictHeight();
28077         this.selectedIndex = -1;
28078     },
28079
28080     // private
28081     onLoad : function(){
28082
28083     
28084         var dom = this.el.dom;
28085         dom.innerHTML = '';
28086          var od = dom.ownerDocument;
28087          
28088         if (this.emptyText) {
28089             var op = od.createElement('option');
28090             op.setAttribute('value', '');
28091             op.innerHTML = String.format('{0}', this.emptyText);
28092             dom.appendChild(op);
28093         }
28094         if(this.store.getCount() > 0){
28095            
28096             var vf = this.valueField;
28097             var df = this.displayField;
28098             this.store.data.each(function(r) {
28099                 // which colmsn to use... testing - cdoe / title..
28100                 var op = od.createElement('option');
28101                 op.setAttribute('value', r.data[vf]);
28102                 op.innerHTML = String.format('{0}', r.data[df]);
28103                 dom.appendChild(op);
28104             });
28105             if (typeof(this.defaultValue != 'undefined')) {
28106                 this.setValue(this.defaultValue);
28107             }
28108             
28109              
28110         }else{
28111             //this.onEmptyResults();
28112         }
28113         //this.el.focus();
28114     },
28115     // private
28116     onLoadException : function()
28117     {
28118         dom.innerHTML = '';
28119             
28120         Roo.log("Select on load exception");
28121         return;
28122     
28123         this.collapse();
28124         Roo.log(this.store.reader.jsonData);
28125         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
28126             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
28127         }
28128         
28129         
28130     },
28131     // private
28132     onTypeAhead : function(){
28133          
28134     },
28135
28136     // private
28137     onSelect : function(record, index){
28138         Roo.log('on select?');
28139         return;
28140         if(this.fireEvent('beforeselect', this, record, index) !== false){
28141             this.setFromData(index > -1 ? record.data : false);
28142             this.collapse();
28143             this.fireEvent('select', this, record, index);
28144         }
28145     },
28146
28147     /**
28148      * Returns the currently selected field value or empty string if no value is set.
28149      * @return {String} value The selected value
28150      */
28151     getValue : function(){
28152         var dom = this.el.dom;
28153         this.value = dom.options[dom.selectedIndex].value;
28154         return this.value;
28155         
28156     },
28157
28158     /**
28159      * Clears any text/value currently set in the field
28160      */
28161     clearValue : function(){
28162         this.value = '';
28163         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
28164         
28165     },
28166
28167     /**
28168      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
28169      * will be displayed in the field.  If the value does not match the data value of an existing item,
28170      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
28171      * Otherwise the field will be blank (although the value will still be set).
28172      * @param {String} value The value to match
28173      */
28174     setValue : function(v){
28175         var d = this.el.dom;
28176         for (var i =0; i < d.options.length;i++) {
28177             if (v == d.options[i].value) {
28178                 d.selectedIndex = i;
28179                 this.value = v;
28180                 return;
28181             }
28182         }
28183         this.clearValue();
28184     },
28185     /**
28186      * @property {Object} the last set data for the element
28187      */
28188     
28189     lastData : false,
28190     /**
28191      * Sets the value of the field based on a object which is related to the record format for the store.
28192      * @param {Object} value the value to set as. or false on reset?
28193      */
28194     setFromData : function(o){
28195         Roo.log('setfrom data?');
28196          
28197         
28198         
28199     },
28200     // private
28201     reset : function(){
28202         this.clearValue();
28203     },
28204     // private
28205     findRecord : function(prop, value){
28206         
28207         return false;
28208     
28209         var record;
28210         if(this.store.getCount() > 0){
28211             this.store.each(function(r){
28212                 if(r.data[prop] == value){
28213                     record = r;
28214                     return false;
28215                 }
28216                 return true;
28217             });
28218         }
28219         return record;
28220     },
28221     
28222     getName: function()
28223     {
28224         // returns hidden if it's set..
28225         if (!this.rendered) {return ''};
28226         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
28227         
28228     },
28229      
28230
28231     
28232
28233     // private
28234     onEmptyResults : function(){
28235         Roo.log('empty results');
28236         //this.collapse();
28237     },
28238
28239     /**
28240      * Returns true if the dropdown list is expanded, else false.
28241      */
28242     isExpanded : function(){
28243         return false;
28244     },
28245
28246     /**
28247      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
28248      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28249      * @param {String} value The data value of the item to select
28250      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28251      * selected item if it is not currently in view (defaults to true)
28252      * @return {Boolean} True if the value matched an item in the list, else false
28253      */
28254     selectByValue : function(v, scrollIntoView){
28255         Roo.log('select By Value');
28256         return false;
28257     
28258         if(v !== undefined && v !== null){
28259             var r = this.findRecord(this.valueField || this.displayField, v);
28260             if(r){
28261                 this.select(this.store.indexOf(r), scrollIntoView);
28262                 return true;
28263             }
28264         }
28265         return false;
28266     },
28267
28268     /**
28269      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
28270      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
28271      * @param {Number} index The zero-based index of the list item to select
28272      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
28273      * selected item if it is not currently in view (defaults to true)
28274      */
28275     select : function(index, scrollIntoView){
28276         Roo.log('select ');
28277         return  ;
28278         
28279         this.selectedIndex = index;
28280         this.view.select(index);
28281         if(scrollIntoView !== false){
28282             var el = this.view.getNode(index);
28283             if(el){
28284                 this.innerList.scrollChildIntoView(el, false);
28285             }
28286         }
28287     },
28288
28289       
28290
28291     // private
28292     validateBlur : function(){
28293         
28294         return;
28295         
28296     },
28297
28298     // private
28299     initQuery : function(){
28300         this.doQuery(this.getRawValue());
28301     },
28302
28303     // private
28304     doForce : function(){
28305         if(this.el.dom.value.length > 0){
28306             this.el.dom.value =
28307                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
28308              
28309         }
28310     },
28311
28312     /**
28313      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
28314      * query allowing the query action to be canceled if needed.
28315      * @param {String} query The SQL query to execute
28316      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
28317      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
28318      * saved in the current store (defaults to false)
28319      */
28320     doQuery : function(q, forceAll){
28321         
28322         Roo.log('doQuery?');
28323         if(q === undefined || q === null){
28324             q = '';
28325         }
28326         var qe = {
28327             query: q,
28328             forceAll: forceAll,
28329             combo: this,
28330             cancel:false
28331         };
28332         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
28333             return false;
28334         }
28335         q = qe.query;
28336         forceAll = qe.forceAll;
28337         if(forceAll === true || (q.length >= this.minChars)){
28338             if(this.lastQuery != q || this.alwaysQuery){
28339                 this.lastQuery = q;
28340                 if(this.mode == 'local'){
28341                     this.selectedIndex = -1;
28342                     if(forceAll){
28343                         this.store.clearFilter();
28344                     }else{
28345                         this.store.filter(this.displayField, q);
28346                     }
28347                     this.onLoad();
28348                 }else{
28349                     this.store.baseParams[this.queryParam] = q;
28350                     this.store.load({
28351                         params: this.getParams(q)
28352                     });
28353                     this.expand();
28354                 }
28355             }else{
28356                 this.selectedIndex = -1;
28357                 this.onLoad();   
28358             }
28359         }
28360     },
28361
28362     // private
28363     getParams : function(q){
28364         var p = {};
28365         //p[this.queryParam] = q;
28366         if(this.pageSize){
28367             p.start = 0;
28368             p.limit = this.pageSize;
28369         }
28370         return p;
28371     },
28372
28373     /**
28374      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
28375      */
28376     collapse : function(){
28377         
28378     },
28379
28380     // private
28381     collapseIf : function(e){
28382         
28383     },
28384
28385     /**
28386      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
28387      */
28388     expand : function(){
28389         
28390     } ,
28391
28392     // private
28393      
28394
28395     /** 
28396     * @cfg {Boolean} grow 
28397     * @hide 
28398     */
28399     /** 
28400     * @cfg {Number} growMin 
28401     * @hide 
28402     */
28403     /** 
28404     * @cfg {Number} growMax 
28405     * @hide 
28406     */
28407     /**
28408      * @hide
28409      * @method autoSize
28410      */
28411     
28412     setWidth : function()
28413     {
28414         
28415     },
28416     getResizeEl : function(){
28417         return this.el;
28418     }
28419 });//<script type="text/javasscript">
28420  
28421
28422 /**
28423  * @class Roo.DDView
28424  * A DnD enabled version of Roo.View.
28425  * @param {Element/String} container The Element in which to create the View.
28426  * @param {String} tpl The template string used to create the markup for each element of the View
28427  * @param {Object} config The configuration properties. These include all the config options of
28428  * {@link Roo.View} plus some specific to this class.<br>
28429  * <p>
28430  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
28431  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
28432  * <p>
28433  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
28434 .x-view-drag-insert-above {
28435         border-top:1px dotted #3366cc;
28436 }
28437 .x-view-drag-insert-below {
28438         border-bottom:1px dotted #3366cc;
28439 }
28440 </code></pre>
28441  * 
28442  */
28443  
28444 Roo.DDView = function(container, tpl, config) {
28445     Roo.DDView.superclass.constructor.apply(this, arguments);
28446     this.getEl().setStyle("outline", "0px none");
28447     this.getEl().unselectable();
28448     if (this.dragGroup) {
28449                 this.setDraggable(this.dragGroup.split(","));
28450     }
28451     if (this.dropGroup) {
28452                 this.setDroppable(this.dropGroup.split(","));
28453     }
28454     if (this.deletable) {
28455         this.setDeletable();
28456     }
28457     this.isDirtyFlag = false;
28458         this.addEvents({
28459                 "drop" : true
28460         });
28461 };
28462
28463 Roo.extend(Roo.DDView, Roo.View, {
28464 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
28465 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
28466 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
28467 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
28468
28469         isFormField: true,
28470
28471         reset: Roo.emptyFn,
28472         
28473         clearInvalid: Roo.form.Field.prototype.clearInvalid,
28474
28475         validate: function() {
28476                 return true;
28477         },
28478         
28479         destroy: function() {
28480                 this.purgeListeners();
28481                 this.getEl.removeAllListeners();
28482                 this.getEl().remove();
28483                 if (this.dragZone) {
28484                         if (this.dragZone.destroy) {
28485                                 this.dragZone.destroy();
28486                         }
28487                 }
28488                 if (this.dropZone) {
28489                         if (this.dropZone.destroy) {
28490                                 this.dropZone.destroy();
28491                         }
28492                 }
28493         },
28494
28495 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
28496         getName: function() {
28497                 return this.name;
28498         },
28499
28500 /**     Loads the View from a JSON string representing the Records to put into the Store. */
28501         setValue: function(v) {
28502                 if (!this.store) {
28503                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
28504                 }
28505                 var data = {};
28506                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
28507                 this.store.proxy = new Roo.data.MemoryProxy(data);
28508                 this.store.load();
28509         },
28510
28511 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
28512         getValue: function() {
28513                 var result = '(';
28514                 this.store.each(function(rec) {
28515                         result += rec.id + ',';
28516                 });
28517                 return result.substr(0, result.length - 1) + ')';
28518         },
28519         
28520         getIds: function() {
28521                 var i = 0, result = new Array(this.store.getCount());
28522                 this.store.each(function(rec) {
28523                         result[i++] = rec.id;
28524                 });
28525                 return result;
28526         },
28527         
28528         isDirty: function() {
28529                 return this.isDirtyFlag;
28530         },
28531
28532 /**
28533  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
28534  *      whole Element becomes the target, and this causes the drop gesture to append.
28535  */
28536     getTargetFromEvent : function(e) {
28537                 var target = e.getTarget();
28538                 while ((target !== null) && (target.parentNode != this.el.dom)) {
28539                 target = target.parentNode;
28540                 }
28541                 if (!target) {
28542                         target = this.el.dom.lastChild || this.el.dom;
28543                 }
28544                 return target;
28545     },
28546
28547 /**
28548  *      Create the drag data which consists of an object which has the property "ddel" as
28549  *      the drag proxy element. 
28550  */
28551     getDragData : function(e) {
28552         var target = this.findItemFromChild(e.getTarget());
28553                 if(target) {
28554                         this.handleSelection(e);
28555                         var selNodes = this.getSelectedNodes();
28556             var dragData = {
28557                 source: this,
28558                 copy: this.copy || (this.allowCopy && e.ctrlKey),
28559                 nodes: selNodes,
28560                 records: []
28561                         };
28562                         var selectedIndices = this.getSelectedIndexes();
28563                         for (var i = 0; i < selectedIndices.length; i++) {
28564                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
28565                         }
28566                         if (selNodes.length == 1) {
28567                                 dragData.ddel = target.cloneNode(true); // the div element
28568                         } else {
28569                                 var div = document.createElement('div'); // create the multi element drag "ghost"
28570                                 div.className = 'multi-proxy';
28571                                 for (var i = 0, len = selNodes.length; i < len; i++) {
28572                                         div.appendChild(selNodes[i].cloneNode(true));
28573                                 }
28574                                 dragData.ddel = div;
28575                         }
28576             //console.log(dragData)
28577             //console.log(dragData.ddel.innerHTML)
28578                         return dragData;
28579                 }
28580         //console.log('nodragData')
28581                 return false;
28582     },
28583     
28584 /**     Specify to which ddGroup items in this DDView may be dragged. */
28585     setDraggable: function(ddGroup) {
28586         if (ddGroup instanceof Array) {
28587                 Roo.each(ddGroup, this.setDraggable, this);
28588                 return;
28589         }
28590         if (this.dragZone) {
28591                 this.dragZone.addToGroup(ddGroup);
28592         } else {
28593                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
28594                                 containerScroll: true,
28595                                 ddGroup: ddGroup 
28596
28597                         });
28598 //                      Draggability implies selection. DragZone's mousedown selects the element.
28599                         if (!this.multiSelect) { this.singleSelect = true; }
28600
28601 //                      Wire the DragZone's handlers up to methods in *this*
28602                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
28603                 }
28604     },
28605
28606 /**     Specify from which ddGroup this DDView accepts drops. */
28607     setDroppable: function(ddGroup) {
28608         if (ddGroup instanceof Array) {
28609                 Roo.each(ddGroup, this.setDroppable, this);
28610                 return;
28611         }
28612         if (this.dropZone) {
28613                 this.dropZone.addToGroup(ddGroup);
28614         } else {
28615                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
28616                                 containerScroll: true,
28617                                 ddGroup: ddGroup
28618                         });
28619
28620 //                      Wire the DropZone's handlers up to methods in *this*
28621                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
28622                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
28623                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
28624                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
28625                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
28626                 }
28627     },
28628
28629 /**     Decide whether to drop above or below a View node. */
28630     getDropPoint : function(e, n, dd){
28631         if (n == this.el.dom) { return "above"; }
28632                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
28633                 var c = t + (b - t) / 2;
28634                 var y = Roo.lib.Event.getPageY(e);
28635                 if(y <= c) {
28636                         return "above";
28637                 }else{
28638                         return "below";
28639                 }
28640     },
28641
28642     onNodeEnter : function(n, dd, e, data){
28643                 return false;
28644     },
28645     
28646     onNodeOver : function(n, dd, e, data){
28647                 var pt = this.getDropPoint(e, n, dd);
28648                 // set the insert point style on the target node
28649                 var dragElClass = this.dropNotAllowed;
28650                 if (pt) {
28651                         var targetElClass;
28652                         if (pt == "above"){
28653                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
28654                                 targetElClass = "x-view-drag-insert-above";
28655                         } else {
28656                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
28657                                 targetElClass = "x-view-drag-insert-below";
28658                         }
28659                         if (this.lastInsertClass != targetElClass){
28660                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
28661                                 this.lastInsertClass = targetElClass;
28662                         }
28663                 }
28664                 return dragElClass;
28665         },
28666
28667     onNodeOut : function(n, dd, e, data){
28668                 this.removeDropIndicators(n);
28669     },
28670
28671     onNodeDrop : function(n, dd, e, data){
28672         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
28673                 return false;
28674         }
28675         var pt = this.getDropPoint(e, n, dd);
28676                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
28677                 if (pt == "below") { insertAt++; }
28678                 for (var i = 0; i < data.records.length; i++) {
28679                         var r = data.records[i];
28680                         var dup = this.store.getById(r.id);
28681                         if (dup && (dd != this.dragZone)) {
28682                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
28683                         } else {
28684                                 if (data.copy) {
28685                                         this.store.insert(insertAt++, r.copy());
28686                                 } else {
28687                                         data.source.isDirtyFlag = true;
28688                                         r.store.remove(r);
28689                                         this.store.insert(insertAt++, r);
28690                                 }
28691                                 this.isDirtyFlag = true;
28692                         }
28693                 }
28694                 this.dragZone.cachedTarget = null;
28695                 return true;
28696     },
28697
28698     removeDropIndicators : function(n){
28699                 if(n){
28700                         Roo.fly(n).removeClass([
28701                                 "x-view-drag-insert-above",
28702                                 "x-view-drag-insert-below"]);
28703                         this.lastInsertClass = "_noclass";
28704                 }
28705     },
28706
28707 /**
28708  *      Utility method. Add a delete option to the DDView's context menu.
28709  *      @param {String} imageUrl The URL of the "delete" icon image.
28710  */
28711         setDeletable: function(imageUrl) {
28712                 if (!this.singleSelect && !this.multiSelect) {
28713                         this.singleSelect = true;
28714                 }
28715                 var c = this.getContextMenu();
28716                 this.contextMenu.on("itemclick", function(item) {
28717                         switch (item.id) {
28718                                 case "delete":
28719                                         this.remove(this.getSelectedIndexes());
28720                                         break;
28721                         }
28722                 }, this);
28723                 this.contextMenu.add({
28724                         icon: imageUrl,
28725                         id: "delete",
28726                         text: 'Delete'
28727                 });
28728         },
28729         
28730 /**     Return the context menu for this DDView. */
28731         getContextMenu: function() {
28732                 if (!this.contextMenu) {
28733 //                      Create the View's context menu
28734                         this.contextMenu = new Roo.menu.Menu({
28735                                 id: this.id + "-contextmenu"
28736                         });
28737                         this.el.on("contextmenu", this.showContextMenu, this);
28738                 }
28739                 return this.contextMenu;
28740         },
28741         
28742         disableContextMenu: function() {
28743                 if (this.contextMenu) {
28744                         this.el.un("contextmenu", this.showContextMenu, this);
28745                 }
28746         },
28747
28748         showContextMenu: function(e, item) {
28749         item = this.findItemFromChild(e.getTarget());
28750                 if (item) {
28751                         e.stopEvent();
28752                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
28753                         this.contextMenu.showAt(e.getXY());
28754             }
28755     },
28756
28757 /**
28758  *      Remove {@link Roo.data.Record}s at the specified indices.
28759  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
28760  */
28761     remove: function(selectedIndices) {
28762                 selectedIndices = [].concat(selectedIndices);
28763                 for (var i = 0; i < selectedIndices.length; i++) {
28764                         var rec = this.store.getAt(selectedIndices[i]);
28765                         this.store.remove(rec);
28766                 }
28767     },
28768
28769 /**
28770  *      Double click fires the event, but also, if this is draggable, and there is only one other
28771  *      related DropZone, it transfers the selected node.
28772  */
28773     onDblClick : function(e){
28774         var item = this.findItemFromChild(e.getTarget());
28775         if(item){
28776             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
28777                 return false;
28778             }
28779             if (this.dragGroup) {
28780                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
28781                     while (targets.indexOf(this.dropZone) > -1) {
28782                             targets.remove(this.dropZone);
28783                                 }
28784                     if (targets.length == 1) {
28785                                         this.dragZone.cachedTarget = null;
28786                         var el = Roo.get(targets[0].getEl());
28787                         var box = el.getBox(true);
28788                         targets[0].onNodeDrop(el.dom, {
28789                                 target: el.dom,
28790                                 xy: [box.x, box.y + box.height - 1]
28791                         }, null, this.getDragData(e));
28792                     }
28793                 }
28794         }
28795     },
28796     
28797     handleSelection: function(e) {
28798                 this.dragZone.cachedTarget = null;
28799         var item = this.findItemFromChild(e.getTarget());
28800         if (!item) {
28801                 this.clearSelections(true);
28802                 return;
28803         }
28804                 if (item && (this.multiSelect || this.singleSelect)){
28805                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
28806                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
28807                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
28808                                 this.unselect(item);
28809                         } else {
28810                                 this.select(item, this.multiSelect && e.ctrlKey);
28811                                 this.lastSelection = item;
28812                         }
28813                 }
28814     },
28815
28816     onItemClick : function(item, index, e){
28817                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
28818                         return false;
28819                 }
28820                 return true;
28821     },
28822
28823     unselect : function(nodeInfo, suppressEvent){
28824                 var node = this.getNode(nodeInfo);
28825                 if(node && this.isSelected(node)){
28826                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28827                                 Roo.fly(node).removeClass(this.selectedClass);
28828                                 this.selections.remove(node);
28829                                 if(!suppressEvent){
28830                                         this.fireEvent("selectionchange", this, this.selections);
28831                                 }
28832                         }
28833                 }
28834     }
28835 });
28836 /*
28837  * Based on:
28838  * Ext JS Library 1.1.1
28839  * Copyright(c) 2006-2007, Ext JS, LLC.
28840  *
28841  * Originally Released Under LGPL - original licence link has changed is not relivant.
28842  *
28843  * Fork - LGPL
28844  * <script type="text/javascript">
28845  */
28846  
28847 /**
28848  * @class Roo.LayoutManager
28849  * @extends Roo.util.Observable
28850  * Base class for layout managers.
28851  */
28852 Roo.LayoutManager = function(container, config){
28853     Roo.LayoutManager.superclass.constructor.call(this);
28854     this.el = Roo.get(container);
28855     // ie scrollbar fix
28856     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
28857         document.body.scroll = "no";
28858     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
28859         this.el.position('relative');
28860     }
28861     this.id = this.el.id;
28862     this.el.addClass("x-layout-container");
28863     /** false to disable window resize monitoring @type Boolean */
28864     this.monitorWindowResize = true;
28865     this.regions = {};
28866     this.addEvents({
28867         /**
28868          * @event layout
28869          * Fires when a layout is performed. 
28870          * @param {Roo.LayoutManager} this
28871          */
28872         "layout" : true,
28873         /**
28874          * @event regionresized
28875          * Fires when the user resizes a region. 
28876          * @param {Roo.LayoutRegion} region The resized region
28877          * @param {Number} newSize The new size (width for east/west, height for north/south)
28878          */
28879         "regionresized" : true,
28880         /**
28881          * @event regioncollapsed
28882          * Fires when a region is collapsed. 
28883          * @param {Roo.LayoutRegion} region The collapsed region
28884          */
28885         "regioncollapsed" : true,
28886         /**
28887          * @event regionexpanded
28888          * Fires when a region is expanded.  
28889          * @param {Roo.LayoutRegion} region The expanded region
28890          */
28891         "regionexpanded" : true
28892     });
28893     this.updating = false;
28894     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
28895 };
28896
28897 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
28898     /**
28899      * Returns true if this layout is currently being updated
28900      * @return {Boolean}
28901      */
28902     isUpdating : function(){
28903         return this.updating; 
28904     },
28905     
28906     /**
28907      * Suspend the LayoutManager from doing auto-layouts while
28908      * making multiple add or remove calls
28909      */
28910     beginUpdate : function(){
28911         this.updating = true;    
28912     },
28913     
28914     /**
28915      * Restore auto-layouts and optionally disable the manager from performing a layout
28916      * @param {Boolean} noLayout true to disable a layout update 
28917      */
28918     endUpdate : function(noLayout){
28919         this.updating = false;
28920         if(!noLayout){
28921             this.layout();
28922         }    
28923     },
28924     
28925     layout: function(){
28926         
28927     },
28928     
28929     onRegionResized : function(region, newSize){
28930         this.fireEvent("regionresized", region, newSize);
28931         this.layout();
28932     },
28933     
28934     onRegionCollapsed : function(region){
28935         this.fireEvent("regioncollapsed", region);
28936     },
28937     
28938     onRegionExpanded : function(region){
28939         this.fireEvent("regionexpanded", region);
28940     },
28941         
28942     /**
28943      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
28944      * performs box-model adjustments.
28945      * @return {Object} The size as an object {width: (the width), height: (the height)}
28946      */
28947     getViewSize : function(){
28948         var size;
28949         if(this.el.dom != document.body){
28950             size = this.el.getSize();
28951         }else{
28952             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
28953         }
28954         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
28955         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28956         return size;
28957     },
28958     
28959     /**
28960      * Returns the Element this layout is bound to.
28961      * @return {Roo.Element}
28962      */
28963     getEl : function(){
28964         return this.el;
28965     },
28966     
28967     /**
28968      * Returns the specified region.
28969      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
28970      * @return {Roo.LayoutRegion}
28971      */
28972     getRegion : function(target){
28973         return this.regions[target.toLowerCase()];
28974     },
28975     
28976     onWindowResize : function(){
28977         if(this.monitorWindowResize){
28978             this.layout();
28979         }
28980     }
28981 });/*
28982  * Based on:
28983  * Ext JS Library 1.1.1
28984  * Copyright(c) 2006-2007, Ext JS, LLC.
28985  *
28986  * Originally Released Under LGPL - original licence link has changed is not relivant.
28987  *
28988  * Fork - LGPL
28989  * <script type="text/javascript">
28990  */
28991 /**
28992  * @class Roo.BorderLayout
28993  * @extends Roo.LayoutManager
28994  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
28995  * please see: <br><br>
28996  * <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>
28997  * <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>
28998  * Example:
28999  <pre><code>
29000  var layout = new Roo.BorderLayout(document.body, {
29001     north: {
29002         initialSize: 25,
29003         titlebar: false
29004     },
29005     west: {
29006         split:true,
29007         initialSize: 200,
29008         minSize: 175,
29009         maxSize: 400,
29010         titlebar: true,
29011         collapsible: true
29012     },
29013     east: {
29014         split:true,
29015         initialSize: 202,
29016         minSize: 175,
29017         maxSize: 400,
29018         titlebar: true,
29019         collapsible: true
29020     },
29021     south: {
29022         split:true,
29023         initialSize: 100,
29024         minSize: 100,
29025         maxSize: 200,
29026         titlebar: true,
29027         collapsible: true
29028     },
29029     center: {
29030         titlebar: true,
29031         autoScroll:true,
29032         resizeTabs: true,
29033         minTabWidth: 50,
29034         preferredTabWidth: 150
29035     }
29036 });
29037
29038 // shorthand
29039 var CP = Roo.ContentPanel;
29040
29041 layout.beginUpdate();
29042 layout.add("north", new CP("north", "North"));
29043 layout.add("south", new CP("south", {title: "South", closable: true}));
29044 layout.add("west", new CP("west", {title: "West"}));
29045 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
29046 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
29047 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
29048 layout.getRegion("center").showPanel("center1");
29049 layout.endUpdate();
29050 </code></pre>
29051
29052 <b>The container the layout is rendered into can be either the body element or any other element.
29053 If it is not the body element, the container needs to either be an absolute positioned element,
29054 or you will need to add "position:relative" to the css of the container.  You will also need to specify
29055 the container size if it is not the body element.</b>
29056
29057 * @constructor
29058 * Create a new BorderLayout
29059 * @param {String/HTMLElement/Element} container The container this layout is bound to
29060 * @param {Object} config Configuration options
29061  */
29062 Roo.BorderLayout = function(container, config){
29063     config = config || {};
29064     Roo.BorderLayout.superclass.constructor.call(this, container, config);
29065     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
29066     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
29067         var target = this.factory.validRegions[i];
29068         if(config[target]){
29069             this.addRegion(target, config[target]);
29070         }
29071     }
29072 };
29073
29074 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
29075     /**
29076      * Creates and adds a new region if it doesn't already exist.
29077      * @param {String} target The target region key (north, south, east, west or center).
29078      * @param {Object} config The regions config object
29079      * @return {BorderLayoutRegion} The new region
29080      */
29081     addRegion : function(target, config){
29082         if(!this.regions[target]){
29083             var r = this.factory.create(target, this, config);
29084             this.bindRegion(target, r);
29085         }
29086         return this.regions[target];
29087     },
29088
29089     // private (kinda)
29090     bindRegion : function(name, r){
29091         this.regions[name] = r;
29092         r.on("visibilitychange", this.layout, this);
29093         r.on("paneladded", this.layout, this);
29094         r.on("panelremoved", this.layout, this);
29095         r.on("invalidated", this.layout, this);
29096         r.on("resized", this.onRegionResized, this);
29097         r.on("collapsed", this.onRegionCollapsed, this);
29098         r.on("expanded", this.onRegionExpanded, this);
29099     },
29100
29101     /**
29102      * Performs a layout update.
29103      */
29104     layout : function(){
29105         if(this.updating) {
29106             return;
29107         }
29108         var size = this.getViewSize();
29109         var w = size.width;
29110         var h = size.height;
29111         var centerW = w;
29112         var centerH = h;
29113         var centerY = 0;
29114         var centerX = 0;
29115         //var x = 0, y = 0;
29116
29117         var rs = this.regions;
29118         var north = rs["north"];
29119         var south = rs["south"]; 
29120         var west = rs["west"];
29121         var east = rs["east"];
29122         var center = rs["center"];
29123         //if(this.hideOnLayout){ // not supported anymore
29124             //c.el.setStyle("display", "none");
29125         //}
29126         if(north && north.isVisible()){
29127             var b = north.getBox();
29128             var m = north.getMargins();
29129             b.width = w - (m.left+m.right);
29130             b.x = m.left;
29131             b.y = m.top;
29132             centerY = b.height + b.y + m.bottom;
29133             centerH -= centerY;
29134             north.updateBox(this.safeBox(b));
29135         }
29136         if(south && south.isVisible()){
29137             var b = south.getBox();
29138             var m = south.getMargins();
29139             b.width = w - (m.left+m.right);
29140             b.x = m.left;
29141             var totalHeight = (b.height + m.top + m.bottom);
29142             b.y = h - totalHeight + m.top;
29143             centerH -= totalHeight;
29144             south.updateBox(this.safeBox(b));
29145         }
29146         if(west && west.isVisible()){
29147             var b = west.getBox();
29148             var m = west.getMargins();
29149             b.height = centerH - (m.top+m.bottom);
29150             b.x = m.left;
29151             b.y = centerY + m.top;
29152             var totalWidth = (b.width + m.left + m.right);
29153             centerX += totalWidth;
29154             centerW -= totalWidth;
29155             west.updateBox(this.safeBox(b));
29156         }
29157         if(east && east.isVisible()){
29158             var b = east.getBox();
29159             var m = east.getMargins();
29160             b.height = centerH - (m.top+m.bottom);
29161             var totalWidth = (b.width + m.left + m.right);
29162             b.x = w - totalWidth + m.left;
29163             b.y = centerY + m.top;
29164             centerW -= totalWidth;
29165             east.updateBox(this.safeBox(b));
29166         }
29167         if(center){
29168             var m = center.getMargins();
29169             var centerBox = {
29170                 x: centerX + m.left,
29171                 y: centerY + m.top,
29172                 width: centerW - (m.left+m.right),
29173                 height: centerH - (m.top+m.bottom)
29174             };
29175             //if(this.hideOnLayout){
29176                 //center.el.setStyle("display", "block");
29177             //}
29178             center.updateBox(this.safeBox(centerBox));
29179         }
29180         this.el.repaint();
29181         this.fireEvent("layout", this);
29182     },
29183
29184     // private
29185     safeBox : function(box){
29186         box.width = Math.max(0, box.width);
29187         box.height = Math.max(0, box.height);
29188         return box;
29189     },
29190
29191     /**
29192      * Adds a ContentPanel (or subclass) to this layout.
29193      * @param {String} target The target region key (north, south, east, west or center).
29194      * @param {Roo.ContentPanel} panel The panel to add
29195      * @return {Roo.ContentPanel} The added panel
29196      */
29197     add : function(target, panel){
29198          
29199         target = target.toLowerCase();
29200         return this.regions[target].add(panel);
29201     },
29202
29203     /**
29204      * Remove a ContentPanel (or subclass) to this layout.
29205      * @param {String} target The target region key (north, south, east, west or center).
29206      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
29207      * @return {Roo.ContentPanel} The removed panel
29208      */
29209     remove : function(target, panel){
29210         target = target.toLowerCase();
29211         return this.regions[target].remove(panel);
29212     },
29213
29214     /**
29215      * Searches all regions for a panel with the specified id
29216      * @param {String} panelId
29217      * @return {Roo.ContentPanel} The panel or null if it wasn't found
29218      */
29219     findPanel : function(panelId){
29220         var rs = this.regions;
29221         for(var target in rs){
29222             if(typeof rs[target] != "function"){
29223                 var p = rs[target].getPanel(panelId);
29224                 if(p){
29225                     return p;
29226                 }
29227             }
29228         }
29229         return null;
29230     },
29231
29232     /**
29233      * Searches all regions for a panel with the specified id and activates (shows) it.
29234      * @param {String/ContentPanel} panelId The panels id or the panel itself
29235      * @return {Roo.ContentPanel} The shown panel or null
29236      */
29237     showPanel : function(panelId) {
29238       var rs = this.regions;
29239       for(var target in rs){
29240          var r = rs[target];
29241          if(typeof r != "function"){
29242             if(r.hasPanel(panelId)){
29243                return r.showPanel(panelId);
29244             }
29245          }
29246       }
29247       return null;
29248    },
29249
29250    /**
29251      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
29252      * @param {Roo.state.Provider} provider (optional) An alternate state provider
29253      */
29254     restoreState : function(provider){
29255         if(!provider){
29256             provider = Roo.state.Manager;
29257         }
29258         var sm = new Roo.LayoutStateManager();
29259         sm.init(this, provider);
29260     },
29261
29262     /**
29263      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
29264      * object should contain properties for each region to add ContentPanels to, and each property's value should be
29265      * a valid ContentPanel config object.  Example:
29266      * <pre><code>
29267 // Create the main layout
29268 var layout = new Roo.BorderLayout('main-ct', {
29269     west: {
29270         split:true,
29271         minSize: 175,
29272         titlebar: true
29273     },
29274     center: {
29275         title:'Components'
29276     }
29277 }, 'main-ct');
29278
29279 // Create and add multiple ContentPanels at once via configs
29280 layout.batchAdd({
29281    west: {
29282        id: 'source-files',
29283        autoCreate:true,
29284        title:'Ext Source Files',
29285        autoScroll:true,
29286        fitToFrame:true
29287    },
29288    center : {
29289        el: cview,
29290        autoScroll:true,
29291        fitToFrame:true,
29292        toolbar: tb,
29293        resizeEl:'cbody'
29294    }
29295 });
29296 </code></pre>
29297      * @param {Object} regions An object containing ContentPanel configs by region name
29298      */
29299     batchAdd : function(regions){
29300         this.beginUpdate();
29301         for(var rname in regions){
29302             var lr = this.regions[rname];
29303             if(lr){
29304                 this.addTypedPanels(lr, regions[rname]);
29305             }
29306         }
29307         this.endUpdate();
29308     },
29309
29310     // private
29311     addTypedPanels : function(lr, ps){
29312         if(typeof ps == 'string'){
29313             lr.add(new Roo.ContentPanel(ps));
29314         }
29315         else if(ps instanceof Array){
29316             for(var i =0, len = ps.length; i < len; i++){
29317                 this.addTypedPanels(lr, ps[i]);
29318             }
29319         }
29320         else if(!ps.events){ // raw config?
29321             var el = ps.el;
29322             delete ps.el; // prevent conflict
29323             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
29324         }
29325         else {  // panel object assumed!
29326             lr.add(ps);
29327         }
29328     },
29329     /**
29330      * Adds a xtype elements to the layout.
29331      * <pre><code>
29332
29333 layout.addxtype({
29334        xtype : 'ContentPanel',
29335        region: 'west',
29336        items: [ .... ]
29337    }
29338 );
29339
29340 layout.addxtype({
29341         xtype : 'NestedLayoutPanel',
29342         region: 'west',
29343         layout: {
29344            center: { },
29345            west: { }   
29346         },
29347         items : [ ... list of content panels or nested layout panels.. ]
29348    }
29349 );
29350 </code></pre>
29351      * @param {Object} cfg Xtype definition of item to add.
29352      */
29353     addxtype : function(cfg)
29354     {
29355         // basically accepts a pannel...
29356         // can accept a layout region..!?!?
29357         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
29358         
29359         if (!cfg.xtype.match(/Panel$/)) {
29360             return false;
29361         }
29362         var ret = false;
29363         
29364         if (typeof(cfg.region) == 'undefined') {
29365             Roo.log("Failed to add Panel, region was not set");
29366             Roo.log(cfg);
29367             return false;
29368         }
29369         var region = cfg.region;
29370         delete cfg.region;
29371         
29372           
29373         var xitems = [];
29374         if (cfg.items) {
29375             xitems = cfg.items;
29376             delete cfg.items;
29377         }
29378         var nb = false;
29379         
29380         switch(cfg.xtype) 
29381         {
29382             case 'ContentPanel':  // ContentPanel (el, cfg)
29383             case 'ScrollPanel':  // ContentPanel (el, cfg)
29384             case 'ViewPanel': 
29385                 if(cfg.autoCreate) {
29386                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29387                 } else {
29388                     var el = this.el.createChild();
29389                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
29390                 }
29391                 
29392                 this.add(region, ret);
29393                 break;
29394             
29395             
29396             case 'TreePanel': // our new panel!
29397                 cfg.el = this.el.createChild();
29398                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29399                 this.add(region, ret);
29400                 break;
29401             
29402             case 'NestedLayoutPanel': 
29403                 // create a new Layout (which is  a Border Layout...
29404                 var el = this.el.createChild();
29405                 var clayout = cfg.layout;
29406                 delete cfg.layout;
29407                 clayout.items   = clayout.items  || [];
29408                 // replace this exitems with the clayout ones..
29409                 xitems = clayout.items;
29410                  
29411                 
29412                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
29413                     cfg.background = false;
29414                 }
29415                 var layout = new Roo.BorderLayout(el, clayout);
29416                 
29417                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
29418                 //console.log('adding nested layout panel '  + cfg.toSource());
29419                 this.add(region, ret);
29420                 nb = {}; /// find first...
29421                 break;
29422                 
29423             case 'GridPanel': 
29424             
29425                 // needs grid and region
29426                 
29427                 //var el = this.getRegion(region).el.createChild();
29428                 var el = this.el.createChild();
29429                 // create the grid first...
29430                 
29431                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
29432                 delete cfg.grid;
29433                 if (region == 'center' && this.active ) {
29434                     cfg.background = false;
29435                 }
29436                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
29437                 
29438                 this.add(region, ret);
29439                 if (cfg.background) {
29440                     ret.on('activate', function(gp) {
29441                         if (!gp.grid.rendered) {
29442                             gp.grid.render();
29443                         }
29444                     });
29445                 } else {
29446                     grid.render();
29447                 }
29448                 break;
29449            
29450            
29451            
29452                 
29453                 
29454                 
29455             default:
29456                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
29457                     
29458                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
29459                     this.add(region, ret);
29460                 } else {
29461                 
29462                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
29463                     return null;
29464                 }
29465                 
29466              // GridPanel (grid, cfg)
29467             
29468         }
29469         this.beginUpdate();
29470         // add children..
29471         var region = '';
29472         var abn = {};
29473         Roo.each(xitems, function(i)  {
29474             region = nb && i.region ? i.region : false;
29475             
29476             var add = ret.addxtype(i);
29477            
29478             if (region) {
29479                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
29480                 if (!i.background) {
29481                     abn[region] = nb[region] ;
29482                 }
29483             }
29484             
29485         });
29486         this.endUpdate();
29487
29488         // make the last non-background panel active..
29489         //if (nb) { Roo.log(abn); }
29490         if (nb) {
29491             
29492             for(var r in abn) {
29493                 region = this.getRegion(r);
29494                 if (region) {
29495                     // tried using nb[r], but it does not work..
29496                      
29497                     region.showPanel(abn[r]);
29498                    
29499                 }
29500             }
29501         }
29502         return ret;
29503         
29504     }
29505 });
29506
29507 /**
29508  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
29509  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
29510  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
29511  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
29512  * <pre><code>
29513 // shorthand
29514 var CP = Roo.ContentPanel;
29515
29516 var layout = Roo.BorderLayout.create({
29517     north: {
29518         initialSize: 25,
29519         titlebar: false,
29520         panels: [new CP("north", "North")]
29521     },
29522     west: {
29523         split:true,
29524         initialSize: 200,
29525         minSize: 175,
29526         maxSize: 400,
29527         titlebar: true,
29528         collapsible: true,
29529         panels: [new CP("west", {title: "West"})]
29530     },
29531     east: {
29532         split:true,
29533         initialSize: 202,
29534         minSize: 175,
29535         maxSize: 400,
29536         titlebar: true,
29537         collapsible: true,
29538         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
29539     },
29540     south: {
29541         split:true,
29542         initialSize: 100,
29543         minSize: 100,
29544         maxSize: 200,
29545         titlebar: true,
29546         collapsible: true,
29547         panels: [new CP("south", {title: "South", closable: true})]
29548     },
29549     center: {
29550         titlebar: true,
29551         autoScroll:true,
29552         resizeTabs: true,
29553         minTabWidth: 50,
29554         preferredTabWidth: 150,
29555         panels: [
29556             new CP("center1", {title: "Close Me", closable: true}),
29557             new CP("center2", {title: "Center Panel", closable: false})
29558         ]
29559     }
29560 }, document.body);
29561
29562 layout.getRegion("center").showPanel("center1");
29563 </code></pre>
29564  * @param config
29565  * @param targetEl
29566  */
29567 Roo.BorderLayout.create = function(config, targetEl){
29568     var layout = new Roo.BorderLayout(targetEl || document.body, config);
29569     layout.beginUpdate();
29570     var regions = Roo.BorderLayout.RegionFactory.validRegions;
29571     for(var j = 0, jlen = regions.length; j < jlen; j++){
29572         var lr = regions[j];
29573         if(layout.regions[lr] && config[lr].panels){
29574             var r = layout.regions[lr];
29575             var ps = config[lr].panels;
29576             layout.addTypedPanels(r, ps);
29577         }
29578     }
29579     layout.endUpdate();
29580     return layout;
29581 };
29582
29583 // private
29584 Roo.BorderLayout.RegionFactory = {
29585     // private
29586     validRegions : ["north","south","east","west","center"],
29587
29588     // private
29589     create : function(target, mgr, config){
29590         target = target.toLowerCase();
29591         if(config.lightweight || config.basic){
29592             return new Roo.BasicLayoutRegion(mgr, config, target);
29593         }
29594         switch(target){
29595             case "north":
29596                 return new Roo.NorthLayoutRegion(mgr, config);
29597             case "south":
29598                 return new Roo.SouthLayoutRegion(mgr, config);
29599             case "east":
29600                 return new Roo.EastLayoutRegion(mgr, config);
29601             case "west":
29602                 return new Roo.WestLayoutRegion(mgr, config);
29603             case "center":
29604                 return new Roo.CenterLayoutRegion(mgr, config);
29605         }
29606         throw 'Layout region "'+target+'" not supported.';
29607     }
29608 };/*
29609  * Based on:
29610  * Ext JS Library 1.1.1
29611  * Copyright(c) 2006-2007, Ext JS, LLC.
29612  *
29613  * Originally Released Under LGPL - original licence link has changed is not relivant.
29614  *
29615  * Fork - LGPL
29616  * <script type="text/javascript">
29617  */
29618  
29619 /**
29620  * @class Roo.BasicLayoutRegion
29621  * @extends Roo.util.Observable
29622  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
29623  * and does not have a titlebar, tabs or any other features. All it does is size and position 
29624  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
29625  */
29626 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
29627     this.mgr = mgr;
29628     this.position  = pos;
29629     this.events = {
29630         /**
29631          * @scope Roo.BasicLayoutRegion
29632          */
29633         
29634         /**
29635          * @event beforeremove
29636          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
29637          * @param {Roo.LayoutRegion} this
29638          * @param {Roo.ContentPanel} panel The panel
29639          * @param {Object} e The cancel event object
29640          */
29641         "beforeremove" : true,
29642         /**
29643          * @event invalidated
29644          * Fires when the layout for this region is changed.
29645          * @param {Roo.LayoutRegion} this
29646          */
29647         "invalidated" : true,
29648         /**
29649          * @event visibilitychange
29650          * Fires when this region is shown or hidden 
29651          * @param {Roo.LayoutRegion} this
29652          * @param {Boolean} visibility true or false
29653          */
29654         "visibilitychange" : true,
29655         /**
29656          * @event paneladded
29657          * Fires when a panel is added. 
29658          * @param {Roo.LayoutRegion} this
29659          * @param {Roo.ContentPanel} panel The panel
29660          */
29661         "paneladded" : true,
29662         /**
29663          * @event panelremoved
29664          * Fires when a panel is removed. 
29665          * @param {Roo.LayoutRegion} this
29666          * @param {Roo.ContentPanel} panel The panel
29667          */
29668         "panelremoved" : true,
29669         /**
29670          * @event beforecollapse
29671          * Fires when this region before collapse.
29672          * @param {Roo.LayoutRegion} this
29673          */
29674         "beforecollapse" : true,
29675         /**
29676          * @event collapsed
29677          * Fires when this region is collapsed.
29678          * @param {Roo.LayoutRegion} this
29679          */
29680         "collapsed" : true,
29681         /**
29682          * @event expanded
29683          * Fires when this region is expanded.
29684          * @param {Roo.LayoutRegion} this
29685          */
29686         "expanded" : true,
29687         /**
29688          * @event slideshow
29689          * Fires when this region is slid into view.
29690          * @param {Roo.LayoutRegion} this
29691          */
29692         "slideshow" : true,
29693         /**
29694          * @event slidehide
29695          * Fires when this region slides out of view. 
29696          * @param {Roo.LayoutRegion} this
29697          */
29698         "slidehide" : true,
29699         /**
29700          * @event panelactivated
29701          * Fires when a panel is activated. 
29702          * @param {Roo.LayoutRegion} this
29703          * @param {Roo.ContentPanel} panel The activated panel
29704          */
29705         "panelactivated" : true,
29706         /**
29707          * @event resized
29708          * Fires when the user resizes this region. 
29709          * @param {Roo.LayoutRegion} this
29710          * @param {Number} newSize The new size (width for east/west, height for north/south)
29711          */
29712         "resized" : true
29713     };
29714     /** A collection of panels in this region. @type Roo.util.MixedCollection */
29715     this.panels = new Roo.util.MixedCollection();
29716     this.panels.getKey = this.getPanelId.createDelegate(this);
29717     this.box = null;
29718     this.activePanel = null;
29719     // ensure listeners are added...
29720     
29721     if (config.listeners || config.events) {
29722         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
29723             listeners : config.listeners || {},
29724             events : config.events || {}
29725         });
29726     }
29727     
29728     if(skipConfig !== true){
29729         this.applyConfig(config);
29730     }
29731 };
29732
29733 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
29734     getPanelId : function(p){
29735         return p.getId();
29736     },
29737     
29738     applyConfig : function(config){
29739         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
29740         this.config = config;
29741         
29742     },
29743     
29744     /**
29745      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
29746      * the width, for horizontal (north, south) the height.
29747      * @param {Number} newSize The new width or height
29748      */
29749     resizeTo : function(newSize){
29750         var el = this.el ? this.el :
29751                  (this.activePanel ? this.activePanel.getEl() : null);
29752         if(el){
29753             switch(this.position){
29754                 case "east":
29755                 case "west":
29756                     el.setWidth(newSize);
29757                     this.fireEvent("resized", this, newSize);
29758                 break;
29759                 case "north":
29760                 case "south":
29761                     el.setHeight(newSize);
29762                     this.fireEvent("resized", this, newSize);
29763                 break;                
29764             }
29765         }
29766     },
29767     
29768     getBox : function(){
29769         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
29770     },
29771     
29772     getMargins : function(){
29773         return this.margins;
29774     },
29775     
29776     updateBox : function(box){
29777         this.box = box;
29778         var el = this.activePanel.getEl();
29779         el.dom.style.left = box.x + "px";
29780         el.dom.style.top = box.y + "px";
29781         this.activePanel.setSize(box.width, box.height);
29782     },
29783     
29784     /**
29785      * Returns the container element for this region.
29786      * @return {Roo.Element}
29787      */
29788     getEl : function(){
29789         return this.activePanel;
29790     },
29791     
29792     /**
29793      * Returns true if this region is currently visible.
29794      * @return {Boolean}
29795      */
29796     isVisible : function(){
29797         return this.activePanel ? true : false;
29798     },
29799     
29800     setActivePanel : function(panel){
29801         panel = this.getPanel(panel);
29802         if(this.activePanel && this.activePanel != panel){
29803             this.activePanel.setActiveState(false);
29804             this.activePanel.getEl().setLeftTop(-10000,-10000);
29805         }
29806         this.activePanel = panel;
29807         panel.setActiveState(true);
29808         if(this.box){
29809             panel.setSize(this.box.width, this.box.height);
29810         }
29811         this.fireEvent("panelactivated", this, panel);
29812         this.fireEvent("invalidated");
29813     },
29814     
29815     /**
29816      * Show the specified panel.
29817      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
29818      * @return {Roo.ContentPanel} The shown panel or null
29819      */
29820     showPanel : function(panel){
29821         if(panel = this.getPanel(panel)){
29822             this.setActivePanel(panel);
29823         }
29824         return panel;
29825     },
29826     
29827     /**
29828      * Get the active panel for this region.
29829      * @return {Roo.ContentPanel} The active panel or null
29830      */
29831     getActivePanel : function(){
29832         return this.activePanel;
29833     },
29834     
29835     /**
29836      * Add the passed ContentPanel(s)
29837      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
29838      * @return {Roo.ContentPanel} The panel added (if only one was added)
29839      */
29840     add : function(panel){
29841         if(arguments.length > 1){
29842             for(var i = 0, len = arguments.length; i < len; i++) {
29843                 this.add(arguments[i]);
29844             }
29845             return null;
29846         }
29847         if(this.hasPanel(panel)){
29848             this.showPanel(panel);
29849             return panel;
29850         }
29851         var el = panel.getEl();
29852         if(el.dom.parentNode != this.mgr.el.dom){
29853             this.mgr.el.dom.appendChild(el.dom);
29854         }
29855         if(panel.setRegion){
29856             panel.setRegion(this);
29857         }
29858         this.panels.add(panel);
29859         el.setStyle("position", "absolute");
29860         if(!panel.background){
29861             this.setActivePanel(panel);
29862             if(this.config.initialSize && this.panels.getCount()==1){
29863                 this.resizeTo(this.config.initialSize);
29864             }
29865         }
29866         this.fireEvent("paneladded", this, panel);
29867         return panel;
29868     },
29869     
29870     /**
29871      * Returns true if the panel is in this region.
29872      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29873      * @return {Boolean}
29874      */
29875     hasPanel : function(panel){
29876         if(typeof panel == "object"){ // must be panel obj
29877             panel = panel.getId();
29878         }
29879         return this.getPanel(panel) ? true : false;
29880     },
29881     
29882     /**
29883      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
29884      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29885      * @param {Boolean} preservePanel Overrides the config preservePanel option
29886      * @return {Roo.ContentPanel} The panel that was removed
29887      */
29888     remove : function(panel, preservePanel){
29889         panel = this.getPanel(panel);
29890         if(!panel){
29891             return null;
29892         }
29893         var e = {};
29894         this.fireEvent("beforeremove", this, panel, e);
29895         if(e.cancel === true){
29896             return null;
29897         }
29898         var panelId = panel.getId();
29899         this.panels.removeKey(panelId);
29900         return panel;
29901     },
29902     
29903     /**
29904      * Returns the panel specified or null if it's not in this region.
29905      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
29906      * @return {Roo.ContentPanel}
29907      */
29908     getPanel : function(id){
29909         if(typeof id == "object"){ // must be panel obj
29910             return id;
29911         }
29912         return this.panels.get(id);
29913     },
29914     
29915     /**
29916      * Returns this regions position (north/south/east/west/center).
29917      * @return {String} 
29918      */
29919     getPosition: function(){
29920         return this.position;    
29921     }
29922 });/*
29923  * Based on:
29924  * Ext JS Library 1.1.1
29925  * Copyright(c) 2006-2007, Ext JS, LLC.
29926  *
29927  * Originally Released Under LGPL - original licence link has changed is not relivant.
29928  *
29929  * Fork - LGPL
29930  * <script type="text/javascript">
29931  */
29932  
29933 /**
29934  * @class Roo.LayoutRegion
29935  * @extends Roo.BasicLayoutRegion
29936  * This class represents a region in a layout manager.
29937  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
29938  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
29939  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
29940  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
29941  * @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})
29942  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
29943  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
29944  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
29945  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
29946  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
29947  * @cfg {String}    title           The title for the region (overrides panel titles)
29948  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
29949  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
29950  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
29951  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
29952  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
29953  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
29954  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
29955  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
29956  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
29957  * @cfg {Boolean}   showPin         True to show a pin button
29958  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
29959  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
29960  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
29961  * @cfg {Number}    width           For East/West panels
29962  * @cfg {Number}    height          For North/South panels
29963  * @cfg {Boolean}   split           To show the splitter
29964  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
29965  */
29966 Roo.LayoutRegion = function(mgr, config, pos){
29967     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
29968     var dh = Roo.DomHelper;
29969     /** This region's container element 
29970     * @type Roo.Element */
29971     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
29972     /** This region's title element 
29973     * @type Roo.Element */
29974
29975     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
29976         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
29977         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
29978     ]}, true);
29979     this.titleEl.enableDisplayMode();
29980     /** This region's title text element 
29981     * @type HTMLElement */
29982     this.titleTextEl = this.titleEl.dom.firstChild;
29983     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
29984     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
29985     this.closeBtn.enableDisplayMode();
29986     this.closeBtn.on("click", this.closeClicked, this);
29987     this.closeBtn.hide();
29988
29989     this.createBody(config);
29990     this.visible = true;
29991     this.collapsed = false;
29992
29993     if(config.hideWhenEmpty){
29994         this.hide();
29995         this.on("paneladded", this.validateVisibility, this);
29996         this.on("panelremoved", this.validateVisibility, this);
29997     }
29998     this.applyConfig(config);
29999 };
30000
30001 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
30002
30003     createBody : function(){
30004         /** This region's body element 
30005         * @type Roo.Element */
30006         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
30007     },
30008
30009     applyConfig : function(c){
30010         if(c.collapsible && this.position != "center" && !this.collapsedEl){
30011             var dh = Roo.DomHelper;
30012             if(c.titlebar !== false){
30013                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
30014                 this.collapseBtn.on("click", this.collapse, this);
30015                 this.collapseBtn.enableDisplayMode();
30016
30017                 if(c.showPin === true || this.showPin){
30018                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
30019                     this.stickBtn.enableDisplayMode();
30020                     this.stickBtn.on("click", this.expand, this);
30021                     this.stickBtn.hide();
30022                 }
30023             }
30024             /** This region's collapsed element
30025             * @type Roo.Element */
30026             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
30027                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
30028             ]}, true);
30029             if(c.floatable !== false){
30030                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
30031                this.collapsedEl.on("click", this.collapseClick, this);
30032             }
30033
30034             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
30035                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
30036                    id: "message", unselectable: "on", style:{"float":"left"}});
30037                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
30038              }
30039             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
30040             this.expandBtn.on("click", this.expand, this);
30041         }
30042         if(this.collapseBtn){
30043             this.collapseBtn.setVisible(c.collapsible == true);
30044         }
30045         this.cmargins = c.cmargins || this.cmargins ||
30046                          (this.position == "west" || this.position == "east" ?
30047                              {top: 0, left: 2, right:2, bottom: 0} :
30048                              {top: 2, left: 0, right:0, bottom: 2});
30049         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
30050         this.bottomTabs = c.tabPosition != "top";
30051         this.autoScroll = c.autoScroll || false;
30052         if(this.autoScroll){
30053             this.bodyEl.setStyle("overflow", "auto");
30054         }else{
30055             this.bodyEl.setStyle("overflow", "hidden");
30056         }
30057         //if(c.titlebar !== false){
30058             if((!c.titlebar && !c.title) || c.titlebar === false){
30059                 this.titleEl.hide();
30060             }else{
30061                 this.titleEl.show();
30062                 if(c.title){
30063                     this.titleTextEl.innerHTML = c.title;
30064                 }
30065             }
30066         //}
30067         this.duration = c.duration || .30;
30068         this.slideDuration = c.slideDuration || .45;
30069         this.config = c;
30070         if(c.collapsed){
30071             this.collapse(true);
30072         }
30073         if(c.hidden){
30074             this.hide();
30075         }
30076     },
30077     /**
30078      * Returns true if this region is currently visible.
30079      * @return {Boolean}
30080      */
30081     isVisible : function(){
30082         return this.visible;
30083     },
30084
30085     /**
30086      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
30087      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
30088      */
30089     setCollapsedTitle : function(title){
30090         title = title || "&#160;";
30091         if(this.collapsedTitleTextEl){
30092             this.collapsedTitleTextEl.innerHTML = title;
30093         }
30094     },
30095
30096     getBox : function(){
30097         var b;
30098         if(!this.collapsed){
30099             b = this.el.getBox(false, true);
30100         }else{
30101             b = this.collapsedEl.getBox(false, true);
30102         }
30103         return b;
30104     },
30105
30106     getMargins : function(){
30107         return this.collapsed ? this.cmargins : this.margins;
30108     },
30109
30110     highlight : function(){
30111         this.el.addClass("x-layout-panel-dragover");
30112     },
30113
30114     unhighlight : function(){
30115         this.el.removeClass("x-layout-panel-dragover");
30116     },
30117
30118     updateBox : function(box){
30119         this.box = box;
30120         if(!this.collapsed){
30121             this.el.dom.style.left = box.x + "px";
30122             this.el.dom.style.top = box.y + "px";
30123             this.updateBody(box.width, box.height);
30124         }else{
30125             this.collapsedEl.dom.style.left = box.x + "px";
30126             this.collapsedEl.dom.style.top = box.y + "px";
30127             this.collapsedEl.setSize(box.width, box.height);
30128         }
30129         if(this.tabs){
30130             this.tabs.autoSizeTabs();
30131         }
30132     },
30133
30134     updateBody : function(w, h){
30135         if(w !== null){
30136             this.el.setWidth(w);
30137             w -= this.el.getBorderWidth("rl");
30138             if(this.config.adjustments){
30139                 w += this.config.adjustments[0];
30140             }
30141         }
30142         if(h !== null){
30143             this.el.setHeight(h);
30144             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
30145             h -= this.el.getBorderWidth("tb");
30146             if(this.config.adjustments){
30147                 h += this.config.adjustments[1];
30148             }
30149             this.bodyEl.setHeight(h);
30150             if(this.tabs){
30151                 h = this.tabs.syncHeight(h);
30152             }
30153         }
30154         if(this.panelSize){
30155             w = w !== null ? w : this.panelSize.width;
30156             h = h !== null ? h : this.panelSize.height;
30157         }
30158         if(this.activePanel){
30159             var el = this.activePanel.getEl();
30160             w = w !== null ? w : el.getWidth();
30161             h = h !== null ? h : el.getHeight();
30162             this.panelSize = {width: w, height: h};
30163             this.activePanel.setSize(w, h);
30164         }
30165         if(Roo.isIE && this.tabs){
30166             this.tabs.el.repaint();
30167         }
30168     },
30169
30170     /**
30171      * Returns the container element for this region.
30172      * @return {Roo.Element}
30173      */
30174     getEl : function(){
30175         return this.el;
30176     },
30177
30178     /**
30179      * Hides this region.
30180      */
30181     hide : function(){
30182         if(!this.collapsed){
30183             this.el.dom.style.left = "-2000px";
30184             this.el.hide();
30185         }else{
30186             this.collapsedEl.dom.style.left = "-2000px";
30187             this.collapsedEl.hide();
30188         }
30189         this.visible = false;
30190         this.fireEvent("visibilitychange", this, false);
30191     },
30192
30193     /**
30194      * Shows this region if it was previously hidden.
30195      */
30196     show : function(){
30197         if(!this.collapsed){
30198             this.el.show();
30199         }else{
30200             this.collapsedEl.show();
30201         }
30202         this.visible = true;
30203         this.fireEvent("visibilitychange", this, true);
30204     },
30205
30206     closeClicked : function(){
30207         if(this.activePanel){
30208             this.remove(this.activePanel);
30209         }
30210     },
30211
30212     collapseClick : function(e){
30213         if(this.isSlid){
30214            e.stopPropagation();
30215            this.slideIn();
30216         }else{
30217            e.stopPropagation();
30218            this.slideOut();
30219         }
30220     },
30221
30222     /**
30223      * Collapses this region.
30224      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
30225      */
30226     collapse : function(skipAnim, skipCheck){
30227         if(this.collapsed) {
30228             return;
30229         }
30230         
30231         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
30232             
30233             this.collapsed = true;
30234             if(this.split){
30235                 this.split.el.hide();
30236             }
30237             if(this.config.animate && skipAnim !== true){
30238                 this.fireEvent("invalidated", this);
30239                 this.animateCollapse();
30240             }else{
30241                 this.el.setLocation(-20000,-20000);
30242                 this.el.hide();
30243                 this.collapsedEl.show();
30244                 this.fireEvent("collapsed", this);
30245                 this.fireEvent("invalidated", this);
30246             }
30247         }
30248         
30249     },
30250
30251     animateCollapse : function(){
30252         // overridden
30253     },
30254
30255     /**
30256      * Expands this region if it was previously collapsed.
30257      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
30258      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
30259      */
30260     expand : function(e, skipAnim){
30261         if(e) {
30262             e.stopPropagation();
30263         }
30264         if(!this.collapsed || this.el.hasActiveFx()) {
30265             return;
30266         }
30267         if(this.isSlid){
30268             this.afterSlideIn();
30269             skipAnim = true;
30270         }
30271         this.collapsed = false;
30272         if(this.config.animate && skipAnim !== true){
30273             this.animateExpand();
30274         }else{
30275             this.el.show();
30276             if(this.split){
30277                 this.split.el.show();
30278             }
30279             this.collapsedEl.setLocation(-2000,-2000);
30280             this.collapsedEl.hide();
30281             this.fireEvent("invalidated", this);
30282             this.fireEvent("expanded", this);
30283         }
30284     },
30285
30286     animateExpand : function(){
30287         // overridden
30288     },
30289
30290     initTabs : function()
30291     {
30292         this.bodyEl.setStyle("overflow", "hidden");
30293         var ts = new Roo.TabPanel(
30294                 this.bodyEl.dom,
30295                 {
30296                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
30297                     disableTooltips: this.config.disableTabTips,
30298                     toolbar : this.config.toolbar
30299                 }
30300         );
30301         if(this.config.hideTabs){
30302             ts.stripWrap.setDisplayed(false);
30303         }
30304         this.tabs = ts;
30305         ts.resizeTabs = this.config.resizeTabs === true;
30306         ts.minTabWidth = this.config.minTabWidth || 40;
30307         ts.maxTabWidth = this.config.maxTabWidth || 250;
30308         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
30309         ts.monitorResize = false;
30310         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30311         ts.bodyEl.addClass('x-layout-tabs-body');
30312         this.panels.each(this.initPanelAsTab, this);
30313     },
30314
30315     initPanelAsTab : function(panel){
30316         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
30317                     this.config.closeOnTab && panel.isClosable());
30318         if(panel.tabTip !== undefined){
30319             ti.setTooltip(panel.tabTip);
30320         }
30321         ti.on("activate", function(){
30322               this.setActivePanel(panel);
30323         }, this);
30324         if(this.config.closeOnTab){
30325             ti.on("beforeclose", function(t, e){
30326                 e.cancel = true;
30327                 this.remove(panel);
30328             }, this);
30329         }
30330         return ti;
30331     },
30332
30333     updatePanelTitle : function(panel, title){
30334         if(this.activePanel == panel){
30335             this.updateTitle(title);
30336         }
30337         if(this.tabs){
30338             var ti = this.tabs.getTab(panel.getEl().id);
30339             ti.setText(title);
30340             if(panel.tabTip !== undefined){
30341                 ti.setTooltip(panel.tabTip);
30342             }
30343         }
30344     },
30345
30346     updateTitle : function(title){
30347         if(this.titleTextEl && !this.config.title){
30348             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
30349         }
30350     },
30351
30352     setActivePanel : function(panel){
30353         panel = this.getPanel(panel);
30354         if(this.activePanel && this.activePanel != panel){
30355             this.activePanel.setActiveState(false);
30356         }
30357         this.activePanel = panel;
30358         panel.setActiveState(true);
30359         if(this.panelSize){
30360             panel.setSize(this.panelSize.width, this.panelSize.height);
30361         }
30362         if(this.closeBtn){
30363             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
30364         }
30365         this.updateTitle(panel.getTitle());
30366         if(this.tabs){
30367             this.fireEvent("invalidated", this);
30368         }
30369         this.fireEvent("panelactivated", this, panel);
30370     },
30371
30372     /**
30373      * Shows the specified panel.
30374      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
30375      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
30376      */
30377     showPanel : function(panel)
30378     {
30379         panel = this.getPanel(panel);
30380         if(panel){
30381             if(this.tabs){
30382                 var tab = this.tabs.getTab(panel.getEl().id);
30383                 if(tab.isHidden()){
30384                     this.tabs.unhideTab(tab.id);
30385                 }
30386                 tab.activate();
30387             }else{
30388                 this.setActivePanel(panel);
30389             }
30390         }
30391         return panel;
30392     },
30393
30394     /**
30395      * Get the active panel for this region.
30396      * @return {Roo.ContentPanel} The active panel or null
30397      */
30398     getActivePanel : function(){
30399         return this.activePanel;
30400     },
30401
30402     validateVisibility : function(){
30403         if(this.panels.getCount() < 1){
30404             this.updateTitle("&#160;");
30405             this.closeBtn.hide();
30406             this.hide();
30407         }else{
30408             if(!this.isVisible()){
30409                 this.show();
30410             }
30411         }
30412     },
30413
30414     /**
30415      * Adds the passed ContentPanel(s) to this region.
30416      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
30417      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
30418      */
30419     add : function(panel){
30420         if(arguments.length > 1){
30421             for(var i = 0, len = arguments.length; i < len; i++) {
30422                 this.add(arguments[i]);
30423             }
30424             return null;
30425         }
30426         if(this.hasPanel(panel)){
30427             this.showPanel(panel);
30428             return panel;
30429         }
30430         panel.setRegion(this);
30431         this.panels.add(panel);
30432         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
30433             this.bodyEl.dom.appendChild(panel.getEl().dom);
30434             if(panel.background !== true){
30435                 this.setActivePanel(panel);
30436             }
30437             this.fireEvent("paneladded", this, panel);
30438             return panel;
30439         }
30440         if(!this.tabs){
30441             this.initTabs();
30442         }else{
30443             this.initPanelAsTab(panel);
30444         }
30445         if(panel.background !== true){
30446             this.tabs.activate(panel.getEl().id);
30447         }
30448         this.fireEvent("paneladded", this, panel);
30449         return panel;
30450     },
30451
30452     /**
30453      * Hides the tab for the specified panel.
30454      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30455      */
30456     hidePanel : function(panel){
30457         if(this.tabs && (panel = this.getPanel(panel))){
30458             this.tabs.hideTab(panel.getEl().id);
30459         }
30460     },
30461
30462     /**
30463      * Unhides the tab for a previously hidden panel.
30464      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30465      */
30466     unhidePanel : function(panel){
30467         if(this.tabs && (panel = this.getPanel(panel))){
30468             this.tabs.unhideTab(panel.getEl().id);
30469         }
30470     },
30471
30472     clearPanels : function(){
30473         while(this.panels.getCount() > 0){
30474              this.remove(this.panels.first());
30475         }
30476     },
30477
30478     /**
30479      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
30480      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
30481      * @param {Boolean} preservePanel Overrides the config preservePanel option
30482      * @return {Roo.ContentPanel} The panel that was removed
30483      */
30484     remove : function(panel, preservePanel){
30485         panel = this.getPanel(panel);
30486         if(!panel){
30487             return null;
30488         }
30489         var e = {};
30490         this.fireEvent("beforeremove", this, panel, e);
30491         if(e.cancel === true){
30492             return null;
30493         }
30494         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
30495         var panelId = panel.getId();
30496         this.panels.removeKey(panelId);
30497         if(preservePanel){
30498             document.body.appendChild(panel.getEl().dom);
30499         }
30500         if(this.tabs){
30501             this.tabs.removeTab(panel.getEl().id);
30502         }else if (!preservePanel){
30503             this.bodyEl.dom.removeChild(panel.getEl().dom);
30504         }
30505         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
30506             var p = this.panels.first();
30507             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
30508             tempEl.appendChild(p.getEl().dom);
30509             this.bodyEl.update("");
30510             this.bodyEl.dom.appendChild(p.getEl().dom);
30511             tempEl = null;
30512             this.updateTitle(p.getTitle());
30513             this.tabs = null;
30514             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
30515             this.setActivePanel(p);
30516         }
30517         panel.setRegion(null);
30518         if(this.activePanel == panel){
30519             this.activePanel = null;
30520         }
30521         if(this.config.autoDestroy !== false && preservePanel !== true){
30522             try{panel.destroy();}catch(e){}
30523         }
30524         this.fireEvent("panelremoved", this, panel);
30525         return panel;
30526     },
30527
30528     /**
30529      * Returns the TabPanel component used by this region
30530      * @return {Roo.TabPanel}
30531      */
30532     getTabs : function(){
30533         return this.tabs;
30534     },
30535
30536     createTool : function(parentEl, className){
30537         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
30538             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
30539         btn.addClassOnOver("x-layout-tools-button-over");
30540         return btn;
30541     }
30542 });/*
30543  * Based on:
30544  * Ext JS Library 1.1.1
30545  * Copyright(c) 2006-2007, Ext JS, LLC.
30546  *
30547  * Originally Released Under LGPL - original licence link has changed is not relivant.
30548  *
30549  * Fork - LGPL
30550  * <script type="text/javascript">
30551  */
30552  
30553
30554
30555 /**
30556  * @class Roo.SplitLayoutRegion
30557  * @extends Roo.LayoutRegion
30558  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
30559  */
30560 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
30561     this.cursor = cursor;
30562     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
30563 };
30564
30565 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
30566     splitTip : "Drag to resize.",
30567     collapsibleSplitTip : "Drag to resize. Double click to hide.",
30568     useSplitTips : false,
30569
30570     applyConfig : function(config){
30571         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
30572         if(config.split){
30573             if(!this.split){
30574                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
30575                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
30576                 /** The SplitBar for this region 
30577                 * @type Roo.SplitBar */
30578                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
30579                 this.split.on("moved", this.onSplitMove, this);
30580                 this.split.useShim = config.useShim === true;
30581                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
30582                 if(this.useSplitTips){
30583                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
30584                 }
30585                 if(config.collapsible){
30586                     this.split.el.on("dblclick", this.collapse,  this);
30587                 }
30588             }
30589             if(typeof config.minSize != "undefined"){
30590                 this.split.minSize = config.minSize;
30591             }
30592             if(typeof config.maxSize != "undefined"){
30593                 this.split.maxSize = config.maxSize;
30594             }
30595             if(config.hideWhenEmpty || config.hidden || config.collapsed){
30596                 this.hideSplitter();
30597             }
30598         }
30599     },
30600
30601     getHMaxSize : function(){
30602          var cmax = this.config.maxSize || 10000;
30603          var center = this.mgr.getRegion("center");
30604          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
30605     },
30606
30607     getVMaxSize : function(){
30608          var cmax = this.config.maxSize || 10000;
30609          var center = this.mgr.getRegion("center");
30610          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
30611     },
30612
30613     onSplitMove : function(split, newSize){
30614         this.fireEvent("resized", this, newSize);
30615     },
30616     
30617     /** 
30618      * Returns the {@link Roo.SplitBar} for this region.
30619      * @return {Roo.SplitBar}
30620      */
30621     getSplitBar : function(){
30622         return this.split;
30623     },
30624     
30625     hide : function(){
30626         this.hideSplitter();
30627         Roo.SplitLayoutRegion.superclass.hide.call(this);
30628     },
30629
30630     hideSplitter : function(){
30631         if(this.split){
30632             this.split.el.setLocation(-2000,-2000);
30633             this.split.el.hide();
30634         }
30635     },
30636
30637     show : function(){
30638         if(this.split){
30639             this.split.el.show();
30640         }
30641         Roo.SplitLayoutRegion.superclass.show.call(this);
30642     },
30643     
30644     beforeSlide: function(){
30645         if(Roo.isGecko){// firefox overflow auto bug workaround
30646             this.bodyEl.clip();
30647             if(this.tabs) {
30648                 this.tabs.bodyEl.clip();
30649             }
30650             if(this.activePanel){
30651                 this.activePanel.getEl().clip();
30652                 
30653                 if(this.activePanel.beforeSlide){
30654                     this.activePanel.beforeSlide();
30655                 }
30656             }
30657         }
30658     },
30659     
30660     afterSlide : function(){
30661         if(Roo.isGecko){// firefox overflow auto bug workaround
30662             this.bodyEl.unclip();
30663             if(this.tabs) {
30664                 this.tabs.bodyEl.unclip();
30665             }
30666             if(this.activePanel){
30667                 this.activePanel.getEl().unclip();
30668                 if(this.activePanel.afterSlide){
30669                     this.activePanel.afterSlide();
30670                 }
30671             }
30672         }
30673     },
30674
30675     initAutoHide : function(){
30676         if(this.autoHide !== false){
30677             if(!this.autoHideHd){
30678                 var st = new Roo.util.DelayedTask(this.slideIn, this);
30679                 this.autoHideHd = {
30680                     "mouseout": function(e){
30681                         if(!e.within(this.el, true)){
30682                             st.delay(500);
30683                         }
30684                     },
30685                     "mouseover" : function(e){
30686                         st.cancel();
30687                     },
30688                     scope : this
30689                 };
30690             }
30691             this.el.on(this.autoHideHd);
30692         }
30693     },
30694
30695     clearAutoHide : function(){
30696         if(this.autoHide !== false){
30697             this.el.un("mouseout", this.autoHideHd.mouseout);
30698             this.el.un("mouseover", this.autoHideHd.mouseover);
30699         }
30700     },
30701
30702     clearMonitor : function(){
30703         Roo.get(document).un("click", this.slideInIf, this);
30704     },
30705
30706     // these names are backwards but not changed for compat
30707     slideOut : function(){
30708         if(this.isSlid || this.el.hasActiveFx()){
30709             return;
30710         }
30711         this.isSlid = true;
30712         if(this.collapseBtn){
30713             this.collapseBtn.hide();
30714         }
30715         this.closeBtnState = this.closeBtn.getStyle('display');
30716         this.closeBtn.hide();
30717         if(this.stickBtn){
30718             this.stickBtn.show();
30719         }
30720         this.el.show();
30721         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
30722         this.beforeSlide();
30723         this.el.setStyle("z-index", 10001);
30724         this.el.slideIn(this.getSlideAnchor(), {
30725             callback: function(){
30726                 this.afterSlide();
30727                 this.initAutoHide();
30728                 Roo.get(document).on("click", this.slideInIf, this);
30729                 this.fireEvent("slideshow", this);
30730             },
30731             scope: this,
30732             block: true
30733         });
30734     },
30735
30736     afterSlideIn : function(){
30737         this.clearAutoHide();
30738         this.isSlid = false;
30739         this.clearMonitor();
30740         this.el.setStyle("z-index", "");
30741         if(this.collapseBtn){
30742             this.collapseBtn.show();
30743         }
30744         this.closeBtn.setStyle('display', this.closeBtnState);
30745         if(this.stickBtn){
30746             this.stickBtn.hide();
30747         }
30748         this.fireEvent("slidehide", this);
30749     },
30750
30751     slideIn : function(cb){
30752         if(!this.isSlid || this.el.hasActiveFx()){
30753             Roo.callback(cb);
30754             return;
30755         }
30756         this.isSlid = false;
30757         this.beforeSlide();
30758         this.el.slideOut(this.getSlideAnchor(), {
30759             callback: function(){
30760                 this.el.setLeftTop(-10000, -10000);
30761                 this.afterSlide();
30762                 this.afterSlideIn();
30763                 Roo.callback(cb);
30764             },
30765             scope: this,
30766             block: true
30767         });
30768     },
30769     
30770     slideInIf : function(e){
30771         if(!e.within(this.el)){
30772             this.slideIn();
30773         }
30774     },
30775
30776     animateCollapse : function(){
30777         this.beforeSlide();
30778         this.el.setStyle("z-index", 20000);
30779         var anchor = this.getSlideAnchor();
30780         this.el.slideOut(anchor, {
30781             callback : function(){
30782                 this.el.setStyle("z-index", "");
30783                 this.collapsedEl.slideIn(anchor, {duration:.3});
30784                 this.afterSlide();
30785                 this.el.setLocation(-10000,-10000);
30786                 this.el.hide();
30787                 this.fireEvent("collapsed", this);
30788             },
30789             scope: this,
30790             block: true
30791         });
30792     },
30793
30794     animateExpand : function(){
30795         this.beforeSlide();
30796         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
30797         this.el.setStyle("z-index", 20000);
30798         this.collapsedEl.hide({
30799             duration:.1
30800         });
30801         this.el.slideIn(this.getSlideAnchor(), {
30802             callback : function(){
30803                 this.el.setStyle("z-index", "");
30804                 this.afterSlide();
30805                 if(this.split){
30806                     this.split.el.show();
30807                 }
30808                 this.fireEvent("invalidated", this);
30809                 this.fireEvent("expanded", this);
30810             },
30811             scope: this,
30812             block: true
30813         });
30814     },
30815
30816     anchors : {
30817         "west" : "left",
30818         "east" : "right",
30819         "north" : "top",
30820         "south" : "bottom"
30821     },
30822
30823     sanchors : {
30824         "west" : "l",
30825         "east" : "r",
30826         "north" : "t",
30827         "south" : "b"
30828     },
30829
30830     canchors : {
30831         "west" : "tl-tr",
30832         "east" : "tr-tl",
30833         "north" : "tl-bl",
30834         "south" : "bl-tl"
30835     },
30836
30837     getAnchor : function(){
30838         return this.anchors[this.position];
30839     },
30840
30841     getCollapseAnchor : function(){
30842         return this.canchors[this.position];
30843     },
30844
30845     getSlideAnchor : function(){
30846         return this.sanchors[this.position];
30847     },
30848
30849     getAlignAdj : function(){
30850         var cm = this.cmargins;
30851         switch(this.position){
30852             case "west":
30853                 return [0, 0];
30854             break;
30855             case "east":
30856                 return [0, 0];
30857             break;
30858             case "north":
30859                 return [0, 0];
30860             break;
30861             case "south":
30862                 return [0, 0];
30863             break;
30864         }
30865     },
30866
30867     getExpandAdj : function(){
30868         var c = this.collapsedEl, cm = this.cmargins;
30869         switch(this.position){
30870             case "west":
30871                 return [-(cm.right+c.getWidth()+cm.left), 0];
30872             break;
30873             case "east":
30874                 return [cm.right+c.getWidth()+cm.left, 0];
30875             break;
30876             case "north":
30877                 return [0, -(cm.top+cm.bottom+c.getHeight())];
30878             break;
30879             case "south":
30880                 return [0, cm.top+cm.bottom+c.getHeight()];
30881             break;
30882         }
30883     }
30884 });/*
30885  * Based on:
30886  * Ext JS Library 1.1.1
30887  * Copyright(c) 2006-2007, Ext JS, LLC.
30888  *
30889  * Originally Released Under LGPL - original licence link has changed is not relivant.
30890  *
30891  * Fork - LGPL
30892  * <script type="text/javascript">
30893  */
30894 /*
30895  * These classes are private internal classes
30896  */
30897 Roo.CenterLayoutRegion = function(mgr, config){
30898     Roo.LayoutRegion.call(this, mgr, config, "center");
30899     this.visible = true;
30900     this.minWidth = config.minWidth || 20;
30901     this.minHeight = config.minHeight || 20;
30902 };
30903
30904 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
30905     hide : function(){
30906         // center panel can't be hidden
30907     },
30908     
30909     show : function(){
30910         // center panel can't be hidden
30911     },
30912     
30913     getMinWidth: function(){
30914         return this.minWidth;
30915     },
30916     
30917     getMinHeight: function(){
30918         return this.minHeight;
30919     }
30920 });
30921
30922
30923 Roo.NorthLayoutRegion = function(mgr, config){
30924     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
30925     if(this.split){
30926         this.split.placement = Roo.SplitBar.TOP;
30927         this.split.orientation = Roo.SplitBar.VERTICAL;
30928         this.split.el.addClass("x-layout-split-v");
30929     }
30930     var size = config.initialSize || config.height;
30931     if(typeof size != "undefined"){
30932         this.el.setHeight(size);
30933     }
30934 };
30935 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
30936     orientation: Roo.SplitBar.VERTICAL,
30937     getBox : function(){
30938         if(this.collapsed){
30939             return this.collapsedEl.getBox();
30940         }
30941         var box = this.el.getBox();
30942         if(this.split){
30943             box.height += this.split.el.getHeight();
30944         }
30945         return box;
30946     },
30947     
30948     updateBox : function(box){
30949         if(this.split && !this.collapsed){
30950             box.height -= this.split.el.getHeight();
30951             this.split.el.setLeft(box.x);
30952             this.split.el.setTop(box.y+box.height);
30953             this.split.el.setWidth(box.width);
30954         }
30955         if(this.collapsed){
30956             this.updateBody(box.width, null);
30957         }
30958         Roo.LayoutRegion.prototype.updateBox.call(this, box);
30959     }
30960 });
30961
30962 Roo.SouthLayoutRegion = function(mgr, config){
30963     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
30964     if(this.split){
30965         this.split.placement = Roo.SplitBar.BOTTOM;
30966         this.split.orientation = Roo.SplitBar.VERTICAL;
30967         this.split.el.addClass("x-layout-split-v");
30968     }
30969     var size = config.initialSize || config.height;
30970     if(typeof size != "undefined"){
30971         this.el.setHeight(size);
30972     }
30973 };
30974 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
30975     orientation: Roo.SplitBar.VERTICAL,
30976     getBox : function(){
30977         if(this.collapsed){
30978             return this.collapsedEl.getBox();
30979         }
30980         var box = this.el.getBox();
30981         if(this.split){
30982             var sh = this.split.el.getHeight();
30983             box.height += sh;
30984             box.y -= sh;
30985         }
30986         return box;
30987     },
30988     
30989     updateBox : function(box){
30990         if(this.split && !this.collapsed){
30991             var sh = this.split.el.getHeight();
30992             box.height -= sh;
30993             box.y += sh;
30994             this.split.el.setLeft(box.x);
30995             this.split.el.setTop(box.y-sh);
30996             this.split.el.setWidth(box.width);
30997         }
30998         if(this.collapsed){
30999             this.updateBody(box.width, null);
31000         }
31001         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31002     }
31003 });
31004
31005 Roo.EastLayoutRegion = function(mgr, config){
31006     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
31007     if(this.split){
31008         this.split.placement = Roo.SplitBar.RIGHT;
31009         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31010         this.split.el.addClass("x-layout-split-h");
31011     }
31012     var size = config.initialSize || config.width;
31013     if(typeof size != "undefined"){
31014         this.el.setWidth(size);
31015     }
31016 };
31017 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
31018     orientation: Roo.SplitBar.HORIZONTAL,
31019     getBox : function(){
31020         if(this.collapsed){
31021             return this.collapsedEl.getBox();
31022         }
31023         var box = this.el.getBox();
31024         if(this.split){
31025             var sw = this.split.el.getWidth();
31026             box.width += sw;
31027             box.x -= sw;
31028         }
31029         return box;
31030     },
31031
31032     updateBox : function(box){
31033         if(this.split && !this.collapsed){
31034             var sw = this.split.el.getWidth();
31035             box.width -= sw;
31036             this.split.el.setLeft(box.x);
31037             this.split.el.setTop(box.y);
31038             this.split.el.setHeight(box.height);
31039             box.x += sw;
31040         }
31041         if(this.collapsed){
31042             this.updateBody(null, box.height);
31043         }
31044         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31045     }
31046 });
31047
31048 Roo.WestLayoutRegion = function(mgr, config){
31049     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
31050     if(this.split){
31051         this.split.placement = Roo.SplitBar.LEFT;
31052         this.split.orientation = Roo.SplitBar.HORIZONTAL;
31053         this.split.el.addClass("x-layout-split-h");
31054     }
31055     var size = config.initialSize || config.width;
31056     if(typeof size != "undefined"){
31057         this.el.setWidth(size);
31058     }
31059 };
31060 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
31061     orientation: Roo.SplitBar.HORIZONTAL,
31062     getBox : function(){
31063         if(this.collapsed){
31064             return this.collapsedEl.getBox();
31065         }
31066         var box = this.el.getBox();
31067         if(this.split){
31068             box.width += this.split.el.getWidth();
31069         }
31070         return box;
31071     },
31072     
31073     updateBox : function(box){
31074         if(this.split && !this.collapsed){
31075             var sw = this.split.el.getWidth();
31076             box.width -= sw;
31077             this.split.el.setLeft(box.x+box.width);
31078             this.split.el.setTop(box.y);
31079             this.split.el.setHeight(box.height);
31080         }
31081         if(this.collapsed){
31082             this.updateBody(null, box.height);
31083         }
31084         Roo.LayoutRegion.prototype.updateBox.call(this, box);
31085     }
31086 });
31087 /*
31088  * Based on:
31089  * Ext JS Library 1.1.1
31090  * Copyright(c) 2006-2007, Ext JS, LLC.
31091  *
31092  * Originally Released Under LGPL - original licence link has changed is not relivant.
31093  *
31094  * Fork - LGPL
31095  * <script type="text/javascript">
31096  */
31097  
31098  
31099 /*
31100  * Private internal class for reading and applying state
31101  */
31102 Roo.LayoutStateManager = function(layout){
31103      // default empty state
31104      this.state = {
31105         north: {},
31106         south: {},
31107         east: {},
31108         west: {}       
31109     };
31110 };
31111
31112 Roo.LayoutStateManager.prototype = {
31113     init : function(layout, provider){
31114         this.provider = provider;
31115         var state = provider.get(layout.id+"-layout-state");
31116         if(state){
31117             var wasUpdating = layout.isUpdating();
31118             if(!wasUpdating){
31119                 layout.beginUpdate();
31120             }
31121             for(var key in state){
31122                 if(typeof state[key] != "function"){
31123                     var rstate = state[key];
31124                     var r = layout.getRegion(key);
31125                     if(r && rstate){
31126                         if(rstate.size){
31127                             r.resizeTo(rstate.size);
31128                         }
31129                         if(rstate.collapsed == true){
31130                             r.collapse(true);
31131                         }else{
31132                             r.expand(null, true);
31133                         }
31134                     }
31135                 }
31136             }
31137             if(!wasUpdating){
31138                 layout.endUpdate();
31139             }
31140             this.state = state; 
31141         }
31142         this.layout = layout;
31143         layout.on("regionresized", this.onRegionResized, this);
31144         layout.on("regioncollapsed", this.onRegionCollapsed, this);
31145         layout.on("regionexpanded", this.onRegionExpanded, this);
31146     },
31147     
31148     storeState : function(){
31149         this.provider.set(this.layout.id+"-layout-state", this.state);
31150     },
31151     
31152     onRegionResized : function(region, newSize){
31153         this.state[region.getPosition()].size = newSize;
31154         this.storeState();
31155     },
31156     
31157     onRegionCollapsed : function(region){
31158         this.state[region.getPosition()].collapsed = true;
31159         this.storeState();
31160     },
31161     
31162     onRegionExpanded : function(region){
31163         this.state[region.getPosition()].collapsed = false;
31164         this.storeState();
31165     }
31166 };/*
31167  * Based on:
31168  * Ext JS Library 1.1.1
31169  * Copyright(c) 2006-2007, Ext JS, LLC.
31170  *
31171  * Originally Released Under LGPL - original licence link has changed is not relivant.
31172  *
31173  * Fork - LGPL
31174  * <script type="text/javascript">
31175  */
31176 /**
31177  * @class Roo.ContentPanel
31178  * @extends Roo.util.Observable
31179  * A basic ContentPanel element.
31180  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
31181  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
31182  * @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
31183  * @cfg {Boolean}   closable      True if the panel can be closed/removed
31184  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
31185  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
31186  * @cfg {Toolbar}   toolbar       A toolbar for this panel
31187  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
31188  * @cfg {String} title          The title for this panel
31189  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
31190  * @cfg {String} url            Calls {@link #setUrl} with this value
31191  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
31192  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
31193  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
31194  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
31195
31196  * @constructor
31197  * Create a new ContentPanel.
31198  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
31199  * @param {String/Object} config A string to set only the title or a config object
31200  * @param {String} content (optional) Set the HTML content for this panel
31201  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
31202  */
31203 Roo.ContentPanel = function(el, config, content){
31204     
31205      
31206     /*
31207     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
31208         config = el;
31209         el = Roo.id();
31210     }
31211     if (config && config.parentLayout) { 
31212         el = config.parentLayout.el.createChild(); 
31213     }
31214     */
31215     if(el.autoCreate){ // xtype is available if this is called from factory
31216         config = el;
31217         el = Roo.id();
31218     }
31219     this.el = Roo.get(el);
31220     if(!this.el && config && config.autoCreate){
31221         if(typeof config.autoCreate == "object"){
31222             if(!config.autoCreate.id){
31223                 config.autoCreate.id = config.id||el;
31224             }
31225             this.el = Roo.DomHelper.append(document.body,
31226                         config.autoCreate, true);
31227         }else{
31228             this.el = Roo.DomHelper.append(document.body,
31229                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
31230         }
31231     }
31232     this.closable = false;
31233     this.loaded = false;
31234     this.active = false;
31235     if(typeof config == "string"){
31236         this.title = config;
31237     }else{
31238         Roo.apply(this, config);
31239     }
31240     
31241     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
31242         this.wrapEl = this.el.wrap();
31243         this.toolbar.container = this.el.insertSibling(false, 'before');
31244         this.toolbar = new Roo.Toolbar(this.toolbar);
31245     }
31246     
31247     // xtype created footer. - not sure if will work as we normally have to render first..
31248     if (this.footer && !this.footer.el && this.footer.xtype) {
31249         if (!this.wrapEl) {
31250             this.wrapEl = this.el.wrap();
31251         }
31252     
31253         this.footer.container = this.wrapEl.createChild();
31254          
31255         this.footer = Roo.factory(this.footer, Roo);
31256         
31257     }
31258     
31259     if(this.resizeEl){
31260         this.resizeEl = Roo.get(this.resizeEl, true);
31261     }else{
31262         this.resizeEl = this.el;
31263     }
31264     // handle view.xtype
31265     
31266  
31267     
31268     
31269     this.addEvents({
31270         /**
31271          * @event activate
31272          * Fires when this panel is activated. 
31273          * @param {Roo.ContentPanel} this
31274          */
31275         "activate" : true,
31276         /**
31277          * @event deactivate
31278          * Fires when this panel is activated. 
31279          * @param {Roo.ContentPanel} this
31280          */
31281         "deactivate" : true,
31282
31283         /**
31284          * @event resize
31285          * Fires when this panel is resized if fitToFrame is true.
31286          * @param {Roo.ContentPanel} this
31287          * @param {Number} width The width after any component adjustments
31288          * @param {Number} height The height after any component adjustments
31289          */
31290         "resize" : true,
31291         
31292          /**
31293          * @event render
31294          * Fires when this tab is created
31295          * @param {Roo.ContentPanel} this
31296          */
31297         "render" : true
31298          
31299         
31300     });
31301     
31302
31303     
31304     
31305     if(this.autoScroll){
31306         this.resizeEl.setStyle("overflow", "auto");
31307     } else {
31308         // fix randome scrolling
31309         this.el.on('scroll', function() {
31310             Roo.log('fix random scolling');
31311             this.scrollTo('top',0); 
31312         });
31313     }
31314     content = content || this.content;
31315     if(content){
31316         this.setContent(content);
31317     }
31318     if(config && config.url){
31319         this.setUrl(this.url, this.params, this.loadOnce);
31320     }
31321     
31322     
31323     
31324     Roo.ContentPanel.superclass.constructor.call(this);
31325     
31326     if (this.view && typeof(this.view.xtype) != 'undefined') {
31327         this.view.el = this.el.appendChild(document.createElement("div"));
31328         this.view = Roo.factory(this.view); 
31329         this.view.render  &&  this.view.render(false, '');  
31330     }
31331     
31332     
31333     this.fireEvent('render', this);
31334 };
31335
31336 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
31337     tabTip:'',
31338     setRegion : function(region){
31339         this.region = region;
31340         if(region){
31341            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
31342         }else{
31343            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
31344         } 
31345     },
31346     
31347     /**
31348      * Returns the toolbar for this Panel if one was configured. 
31349      * @return {Roo.Toolbar} 
31350      */
31351     getToolbar : function(){
31352         return this.toolbar;
31353     },
31354     
31355     setActiveState : function(active){
31356         this.active = active;
31357         if(!active){
31358             this.fireEvent("deactivate", this);
31359         }else{
31360             this.fireEvent("activate", this);
31361         }
31362     },
31363     /**
31364      * Updates this panel's element
31365      * @param {String} content The new content
31366      * @param {Boolean} loadScripts (optional) true to look for and process scripts
31367     */
31368     setContent : function(content, loadScripts){
31369         this.el.update(content, loadScripts);
31370     },
31371
31372     ignoreResize : function(w, h){
31373         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
31374             return true;
31375         }else{
31376             this.lastSize = {width: w, height: h};
31377             return false;
31378         }
31379     },
31380     /**
31381      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
31382      * @return {Roo.UpdateManager} The UpdateManager
31383      */
31384     getUpdateManager : function(){
31385         return this.el.getUpdateManager();
31386     },
31387      /**
31388      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
31389      * @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:
31390 <pre><code>
31391 panel.load({
31392     url: "your-url.php",
31393     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
31394     callback: yourFunction,
31395     scope: yourObject, //(optional scope)
31396     discardUrl: false,
31397     nocache: false,
31398     text: "Loading...",
31399     timeout: 30,
31400     scripts: false
31401 });
31402 </code></pre>
31403      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
31404      * 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.
31405      * @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}
31406      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
31407      * @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.
31408      * @return {Roo.ContentPanel} this
31409      */
31410     load : function(){
31411         var um = this.el.getUpdateManager();
31412         um.update.apply(um, arguments);
31413         return this;
31414     },
31415
31416
31417     /**
31418      * 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.
31419      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
31420      * @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)
31421      * @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)
31422      * @return {Roo.UpdateManager} The UpdateManager
31423      */
31424     setUrl : function(url, params, loadOnce){
31425         if(this.refreshDelegate){
31426             this.removeListener("activate", this.refreshDelegate);
31427         }
31428         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
31429         this.on("activate", this.refreshDelegate);
31430         return this.el.getUpdateManager();
31431     },
31432     
31433     _handleRefresh : function(url, params, loadOnce){
31434         if(!loadOnce || !this.loaded){
31435             var updater = this.el.getUpdateManager();
31436             updater.update(url, params, this._setLoaded.createDelegate(this));
31437         }
31438     },
31439     
31440     _setLoaded : function(){
31441         this.loaded = true;
31442     }, 
31443     
31444     /**
31445      * Returns this panel's id
31446      * @return {String} 
31447      */
31448     getId : function(){
31449         return this.el.id;
31450     },
31451     
31452     /** 
31453      * Returns this panel's element - used by regiosn to add.
31454      * @return {Roo.Element} 
31455      */
31456     getEl : function(){
31457         return this.wrapEl || this.el;
31458     },
31459     
31460     adjustForComponents : function(width, height)
31461     {
31462         //Roo.log('adjustForComponents ');
31463         if(this.resizeEl != this.el){
31464             width -= this.el.getFrameWidth('lr');
31465             height -= this.el.getFrameWidth('tb');
31466         }
31467         if(this.toolbar){
31468             var te = this.toolbar.getEl();
31469             height -= te.getHeight();
31470             te.setWidth(width);
31471         }
31472         if(this.footer){
31473             var te = this.footer.getEl();
31474             //Roo.log("footer:" + te.getHeight());
31475             
31476             height -= te.getHeight();
31477             te.setWidth(width);
31478         }
31479         
31480         
31481         if(this.adjustments){
31482             width += this.adjustments[0];
31483             height += this.adjustments[1];
31484         }
31485         return {"width": width, "height": height};
31486     },
31487     
31488     setSize : function(width, height){
31489         if(this.fitToFrame && !this.ignoreResize(width, height)){
31490             if(this.fitContainer && this.resizeEl != this.el){
31491                 this.el.setSize(width, height);
31492             }
31493             var size = this.adjustForComponents(width, height);
31494             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
31495             this.fireEvent('resize', this, size.width, size.height);
31496         }
31497     },
31498     
31499     /**
31500      * Returns this panel's title
31501      * @return {String} 
31502      */
31503     getTitle : function(){
31504         return this.title;
31505     },
31506     
31507     /**
31508      * Set this panel's title
31509      * @param {String} title
31510      */
31511     setTitle : function(title){
31512         this.title = title;
31513         if(this.region){
31514             this.region.updatePanelTitle(this, title);
31515         }
31516     },
31517     
31518     /**
31519      * Returns true is this panel was configured to be closable
31520      * @return {Boolean} 
31521      */
31522     isClosable : function(){
31523         return this.closable;
31524     },
31525     
31526     beforeSlide : function(){
31527         this.el.clip();
31528         this.resizeEl.clip();
31529     },
31530     
31531     afterSlide : function(){
31532         this.el.unclip();
31533         this.resizeEl.unclip();
31534     },
31535     
31536     /**
31537      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
31538      *   Will fail silently if the {@link #setUrl} method has not been called.
31539      *   This does not activate the panel, just updates its content.
31540      */
31541     refresh : function(){
31542         if(this.refreshDelegate){
31543            this.loaded = false;
31544            this.refreshDelegate();
31545         }
31546     },
31547     
31548     /**
31549      * Destroys this panel
31550      */
31551     destroy : function(){
31552         this.el.removeAllListeners();
31553         var tempEl = document.createElement("span");
31554         tempEl.appendChild(this.el.dom);
31555         tempEl.innerHTML = "";
31556         this.el.remove();
31557         this.el = null;
31558     },
31559     
31560     /**
31561      * form - if the content panel contains a form - this is a reference to it.
31562      * @type {Roo.form.Form}
31563      */
31564     form : false,
31565     /**
31566      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
31567      *    This contains a reference to it.
31568      * @type {Roo.View}
31569      */
31570     view : false,
31571     
31572       /**
31573      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
31574      * <pre><code>
31575
31576 layout.addxtype({
31577        xtype : 'Form',
31578        items: [ .... ]
31579    }
31580 );
31581
31582 </code></pre>
31583      * @param {Object} cfg Xtype definition of item to add.
31584      */
31585     
31586     addxtype : function(cfg) {
31587         // add form..
31588         if (cfg.xtype.match(/^Form$/)) {
31589             
31590             var el;
31591             //if (this.footer) {
31592             //    el = this.footer.container.insertSibling(false, 'before');
31593             //} else {
31594                 el = this.el.createChild();
31595             //}
31596
31597             this.form = new  Roo.form.Form(cfg);
31598             
31599             
31600             if ( this.form.allItems.length) {
31601                 this.form.render(el.dom);
31602             }
31603             return this.form;
31604         }
31605         // should only have one of theses..
31606         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
31607             // views.. should not be just added - used named prop 'view''
31608             
31609             cfg.el = this.el.appendChild(document.createElement("div"));
31610             // factory?
31611             
31612             var ret = new Roo.factory(cfg);
31613              
31614              ret.render && ret.render(false, ''); // render blank..
31615             this.view = ret;
31616             return ret;
31617         }
31618         return false;
31619     }
31620 });
31621
31622 /**
31623  * @class Roo.GridPanel
31624  * @extends Roo.ContentPanel
31625  * @constructor
31626  * Create a new GridPanel.
31627  * @param {Roo.grid.Grid} grid The grid for this panel
31628  * @param {String/Object} config A string to set only the panel's title, or a config object
31629  */
31630 Roo.GridPanel = function(grid, config){
31631     
31632   
31633     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
31634         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
31635         
31636     this.wrapper.dom.appendChild(grid.getGridEl().dom);
31637     
31638     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
31639     
31640     if(this.toolbar){
31641         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
31642     }
31643     // xtype created footer. - not sure if will work as we normally have to render first..
31644     if (this.footer && !this.footer.el && this.footer.xtype) {
31645         
31646         this.footer.container = this.grid.getView().getFooterPanel(true);
31647         this.footer.dataSource = this.grid.dataSource;
31648         this.footer = Roo.factory(this.footer, Roo);
31649         
31650     }
31651     
31652     grid.monitorWindowResize = false; // turn off autosizing
31653     grid.autoHeight = false;
31654     grid.autoWidth = false;
31655     this.grid = grid;
31656     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
31657 };
31658
31659 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
31660     getId : function(){
31661         return this.grid.id;
31662     },
31663     
31664     /**
31665      * Returns the grid for this panel
31666      * @return {Roo.grid.Grid} 
31667      */
31668     getGrid : function(){
31669         return this.grid;    
31670     },
31671     
31672     setSize : function(width, height){
31673         if(!this.ignoreResize(width, height)){
31674             var grid = this.grid;
31675             var size = this.adjustForComponents(width, height);
31676             grid.getGridEl().setSize(size.width, size.height);
31677             grid.autoSize();
31678         }
31679     },
31680     
31681     beforeSlide : function(){
31682         this.grid.getView().scroller.clip();
31683     },
31684     
31685     afterSlide : function(){
31686         this.grid.getView().scroller.unclip();
31687     },
31688     
31689     destroy : function(){
31690         this.grid.destroy();
31691         delete this.grid;
31692         Roo.GridPanel.superclass.destroy.call(this); 
31693     }
31694 });
31695
31696
31697 /**
31698  * @class Roo.NestedLayoutPanel
31699  * @extends Roo.ContentPanel
31700  * @constructor
31701  * Create a new NestedLayoutPanel.
31702  * 
31703  * 
31704  * @param {Roo.BorderLayout} layout The layout for this panel
31705  * @param {String/Object} config A string to set only the title or a config object
31706  */
31707 Roo.NestedLayoutPanel = function(layout, config)
31708 {
31709     // construct with only one argument..
31710     /* FIXME - implement nicer consturctors
31711     if (layout.layout) {
31712         config = layout;
31713         layout = config.layout;
31714         delete config.layout;
31715     }
31716     if (layout.xtype && !layout.getEl) {
31717         // then layout needs constructing..
31718         layout = Roo.factory(layout, Roo);
31719     }
31720     */
31721     
31722     
31723     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
31724     
31725     layout.monitorWindowResize = false; // turn off autosizing
31726     this.layout = layout;
31727     this.layout.getEl().addClass("x-layout-nested-layout");
31728     
31729     
31730     
31731     
31732 };
31733
31734 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
31735
31736     setSize : function(width, height){
31737         if(!this.ignoreResize(width, height)){
31738             var size = this.adjustForComponents(width, height);
31739             var el = this.layout.getEl();
31740             el.setSize(size.width, size.height);
31741             var touch = el.dom.offsetWidth;
31742             this.layout.layout();
31743             // ie requires a double layout on the first pass
31744             if(Roo.isIE && !this.initialized){
31745                 this.initialized = true;
31746                 this.layout.layout();
31747             }
31748         }
31749     },
31750     
31751     // activate all subpanels if not currently active..
31752     
31753     setActiveState : function(active){
31754         this.active = active;
31755         if(!active){
31756             this.fireEvent("deactivate", this);
31757             return;
31758         }
31759         
31760         this.fireEvent("activate", this);
31761         // not sure if this should happen before or after..
31762         if (!this.layout) {
31763             return; // should not happen..
31764         }
31765         var reg = false;
31766         for (var r in this.layout.regions) {
31767             reg = this.layout.getRegion(r);
31768             if (reg.getActivePanel()) {
31769                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
31770                 reg.setActivePanel(reg.getActivePanel());
31771                 continue;
31772             }
31773             if (!reg.panels.length) {
31774                 continue;
31775             }
31776             reg.showPanel(reg.getPanel(0));
31777         }
31778         
31779         
31780         
31781         
31782     },
31783     
31784     /**
31785      * Returns the nested BorderLayout for this panel
31786      * @return {Roo.BorderLayout} 
31787      */
31788     getLayout : function(){
31789         return this.layout;
31790     },
31791     
31792      /**
31793      * Adds a xtype elements to the layout of the nested panel
31794      * <pre><code>
31795
31796 panel.addxtype({
31797        xtype : 'ContentPanel',
31798        region: 'west',
31799        items: [ .... ]
31800    }
31801 );
31802
31803 panel.addxtype({
31804         xtype : 'NestedLayoutPanel',
31805         region: 'west',
31806         layout: {
31807            center: { },
31808            west: { }   
31809         },
31810         items : [ ... list of content panels or nested layout panels.. ]
31811    }
31812 );
31813 </code></pre>
31814      * @param {Object} cfg Xtype definition of item to add.
31815      */
31816     addxtype : function(cfg) {
31817         return this.layout.addxtype(cfg);
31818     
31819     }
31820 });
31821
31822 Roo.ScrollPanel = function(el, config, content){
31823     config = config || {};
31824     config.fitToFrame = true;
31825     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
31826     
31827     this.el.dom.style.overflow = "hidden";
31828     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
31829     this.el.removeClass("x-layout-inactive-content");
31830     this.el.on("mousewheel", this.onWheel, this);
31831
31832     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
31833     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
31834     up.unselectable(); down.unselectable();
31835     up.on("click", this.scrollUp, this);
31836     down.on("click", this.scrollDown, this);
31837     up.addClassOnOver("x-scroller-btn-over");
31838     down.addClassOnOver("x-scroller-btn-over");
31839     up.addClassOnClick("x-scroller-btn-click");
31840     down.addClassOnClick("x-scroller-btn-click");
31841     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
31842
31843     this.resizeEl = this.el;
31844     this.el = wrap; this.up = up; this.down = down;
31845 };
31846
31847 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
31848     increment : 100,
31849     wheelIncrement : 5,
31850     scrollUp : function(){
31851         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
31852     },
31853
31854     scrollDown : function(){
31855         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
31856     },
31857
31858     afterScroll : function(){
31859         var el = this.resizeEl;
31860         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
31861         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31862         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
31863     },
31864
31865     setSize : function(){
31866         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
31867         this.afterScroll();
31868     },
31869
31870     onWheel : function(e){
31871         var d = e.getWheelDelta();
31872         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
31873         this.afterScroll();
31874         e.stopEvent();
31875     },
31876
31877     setContent : function(content, loadScripts){
31878         this.resizeEl.update(content, loadScripts);
31879     }
31880
31881 });
31882
31883
31884
31885
31886
31887
31888
31889
31890
31891 /**
31892  * @class Roo.TreePanel
31893  * @extends Roo.ContentPanel
31894  * @constructor
31895  * Create a new TreePanel. - defaults to fit/scoll contents.
31896  * @param {String/Object} config A string to set only the panel's title, or a config object
31897  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
31898  */
31899 Roo.TreePanel = function(config){
31900     var el = config.el;
31901     var tree = config.tree;
31902     delete config.tree; 
31903     delete config.el; // hopefull!
31904     
31905     // wrapper for IE7 strict & safari scroll issue
31906     
31907     var treeEl = el.createChild();
31908     config.resizeEl = treeEl;
31909     
31910     
31911     
31912     Roo.TreePanel.superclass.constructor.call(this, el, config);
31913  
31914  
31915     this.tree = new Roo.tree.TreePanel(treeEl , tree);
31916     //console.log(tree);
31917     this.on('activate', function()
31918     {
31919         if (this.tree.rendered) {
31920             return;
31921         }
31922         //console.log('render tree');
31923         this.tree.render();
31924     });
31925     // this should not be needed.. - it's actually the 'el' that resizes?
31926     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
31927     
31928     //this.on('resize',  function (cp, w, h) {
31929     //        this.tree.innerCt.setWidth(w);
31930     //        this.tree.innerCt.setHeight(h);
31931     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
31932     //});
31933
31934         
31935     
31936 };
31937
31938 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
31939     fitToFrame : true,
31940     autoScroll : true
31941 });
31942
31943
31944
31945
31946
31947
31948
31949
31950
31951
31952
31953 /*
31954  * Based on:
31955  * Ext JS Library 1.1.1
31956  * Copyright(c) 2006-2007, Ext JS, LLC.
31957  *
31958  * Originally Released Under LGPL - original licence link has changed is not relivant.
31959  *
31960  * Fork - LGPL
31961  * <script type="text/javascript">
31962  */
31963  
31964
31965 /**
31966  * @class Roo.ReaderLayout
31967  * @extends Roo.BorderLayout
31968  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
31969  * center region containing two nested regions (a top one for a list view and one for item preview below),
31970  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
31971  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
31972  * expedites the setup of the overall layout and regions for this common application style.
31973  * Example:
31974  <pre><code>
31975 var reader = new Roo.ReaderLayout();
31976 var CP = Roo.ContentPanel;  // shortcut for adding
31977
31978 reader.beginUpdate();
31979 reader.add("north", new CP("north", "North"));
31980 reader.add("west", new CP("west", {title: "West"}));
31981 reader.add("east", new CP("east", {title: "East"}));
31982
31983 reader.regions.listView.add(new CP("listView", "List"));
31984 reader.regions.preview.add(new CP("preview", "Preview"));
31985 reader.endUpdate();
31986 </code></pre>
31987 * @constructor
31988 * Create a new ReaderLayout
31989 * @param {Object} config Configuration options
31990 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
31991 * document.body if omitted)
31992 */
31993 Roo.ReaderLayout = function(config, renderTo){
31994     var c = config || {size:{}};
31995     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
31996         north: c.north !== false ? Roo.apply({
31997             split:false,
31998             initialSize: 32,
31999             titlebar: false
32000         }, c.north) : false,
32001         west: c.west !== false ? Roo.apply({
32002             split:true,
32003             initialSize: 200,
32004             minSize: 175,
32005             maxSize: 400,
32006             titlebar: true,
32007             collapsible: true,
32008             animate: true,
32009             margins:{left:5,right:0,bottom:5,top:5},
32010             cmargins:{left:5,right:5,bottom:5,top:5}
32011         }, c.west) : false,
32012         east: c.east !== false ? Roo.apply({
32013             split:true,
32014             initialSize: 200,
32015             minSize: 175,
32016             maxSize: 400,
32017             titlebar: true,
32018             collapsible: true,
32019             animate: true,
32020             margins:{left:0,right:5,bottom:5,top:5},
32021             cmargins:{left:5,right:5,bottom:5,top:5}
32022         }, c.east) : false,
32023         center: Roo.apply({
32024             tabPosition: 'top',
32025             autoScroll:false,
32026             closeOnTab: true,
32027             titlebar:false,
32028             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
32029         }, c.center)
32030     });
32031
32032     this.el.addClass('x-reader');
32033
32034     this.beginUpdate();
32035
32036     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
32037         south: c.preview !== false ? Roo.apply({
32038             split:true,
32039             initialSize: 200,
32040             minSize: 100,
32041             autoScroll:true,
32042             collapsible:true,
32043             titlebar: true,
32044             cmargins:{top:5,left:0, right:0, bottom:0}
32045         }, c.preview) : false,
32046         center: Roo.apply({
32047             autoScroll:false,
32048             titlebar:false,
32049             minHeight:200
32050         }, c.listView)
32051     });
32052     this.add('center', new Roo.NestedLayoutPanel(inner,
32053             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
32054
32055     this.endUpdate();
32056
32057     this.regions.preview = inner.getRegion('south');
32058     this.regions.listView = inner.getRegion('center');
32059 };
32060
32061 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
32062  * Based on:
32063  * Ext JS Library 1.1.1
32064  * Copyright(c) 2006-2007, Ext JS, LLC.
32065  *
32066  * Originally Released Under LGPL - original licence link has changed is not relivant.
32067  *
32068  * Fork - LGPL
32069  * <script type="text/javascript">
32070  */
32071  
32072 /**
32073  * @class Roo.grid.Grid
32074  * @extends Roo.util.Observable
32075  * This class represents the primary interface of a component based grid control.
32076  * <br><br>Usage:<pre><code>
32077  var grid = new Roo.grid.Grid("my-container-id", {
32078      ds: myDataStore,
32079      cm: myColModel,
32080      selModel: mySelectionModel,
32081      autoSizeColumns: true,
32082      monitorWindowResize: false,
32083      trackMouseOver: true
32084  });
32085  // set any options
32086  grid.render();
32087  * </code></pre>
32088  * <b>Common Problems:</b><br/>
32089  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
32090  * element will correct this<br/>
32091  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
32092  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
32093  * are unpredictable.<br/>
32094  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
32095  * grid to calculate dimensions/offsets.<br/>
32096   * @constructor
32097  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
32098  * The container MUST have some type of size defined for the grid to fill. The container will be
32099  * automatically set to position relative if it isn't already.
32100  * @param {Object} config A config object that sets properties on this grid.
32101  */
32102 Roo.grid.Grid = function(container, config){
32103         // initialize the container
32104         this.container = Roo.get(container);
32105         this.container.update("");
32106         this.container.setStyle("overflow", "hidden");
32107     this.container.addClass('x-grid-container');
32108
32109     this.id = this.container.id;
32110
32111     Roo.apply(this, config);
32112     // check and correct shorthanded configs
32113     if(this.ds){
32114         this.dataSource = this.ds;
32115         delete this.ds;
32116     }
32117     if(this.cm){
32118         this.colModel = this.cm;
32119         delete this.cm;
32120     }
32121     if(this.sm){
32122         this.selModel = this.sm;
32123         delete this.sm;
32124     }
32125
32126     if (this.selModel) {
32127         this.selModel = Roo.factory(this.selModel, Roo.grid);
32128         this.sm = this.selModel;
32129         this.sm.xmodule = this.xmodule || false;
32130     }
32131     if (typeof(this.colModel.config) == 'undefined') {
32132         this.colModel = new Roo.grid.ColumnModel(this.colModel);
32133         this.cm = this.colModel;
32134         this.cm.xmodule = this.xmodule || false;
32135     }
32136     if (this.dataSource) {
32137         this.dataSource= Roo.factory(this.dataSource, Roo.data);
32138         this.ds = this.dataSource;
32139         this.ds.xmodule = this.xmodule || false;
32140          
32141     }
32142     
32143     
32144     
32145     if(this.width){
32146         this.container.setWidth(this.width);
32147     }
32148
32149     if(this.height){
32150         this.container.setHeight(this.height);
32151     }
32152     /** @private */
32153         this.addEvents({
32154         // raw events
32155         /**
32156          * @event click
32157          * The raw click event for the entire grid.
32158          * @param {Roo.EventObject} e
32159          */
32160         "click" : true,
32161         /**
32162          * @event dblclick
32163          * The raw dblclick event for the entire grid.
32164          * @param {Roo.EventObject} e
32165          */
32166         "dblclick" : true,
32167         /**
32168          * @event contextmenu
32169          * The raw contextmenu event for the entire grid.
32170          * @param {Roo.EventObject} e
32171          */
32172         "contextmenu" : true,
32173         /**
32174          * @event mousedown
32175          * The raw mousedown event for the entire grid.
32176          * @param {Roo.EventObject} e
32177          */
32178         "mousedown" : true,
32179         /**
32180          * @event mouseup
32181          * The raw mouseup event for the entire grid.
32182          * @param {Roo.EventObject} e
32183          */
32184         "mouseup" : true,
32185         /**
32186          * @event mouseover
32187          * The raw mouseover event for the entire grid.
32188          * @param {Roo.EventObject} e
32189          */
32190         "mouseover" : true,
32191         /**
32192          * @event mouseout
32193          * The raw mouseout event for the entire grid.
32194          * @param {Roo.EventObject} e
32195          */
32196         "mouseout" : true,
32197         /**
32198          * @event keypress
32199          * The raw keypress event for the entire grid.
32200          * @param {Roo.EventObject} e
32201          */
32202         "keypress" : true,
32203         /**
32204          * @event keydown
32205          * The raw keydown event for the entire grid.
32206          * @param {Roo.EventObject} e
32207          */
32208         "keydown" : true,
32209
32210         // custom events
32211
32212         /**
32213          * @event cellclick
32214          * Fires when a cell is clicked
32215          * @param {Grid} this
32216          * @param {Number} rowIndex
32217          * @param {Number} columnIndex
32218          * @param {Roo.EventObject} e
32219          */
32220         "cellclick" : true,
32221         /**
32222          * @event celldblclick
32223          * Fires when a cell is double clicked
32224          * @param {Grid} this
32225          * @param {Number} rowIndex
32226          * @param {Number} columnIndex
32227          * @param {Roo.EventObject} e
32228          */
32229         "celldblclick" : true,
32230         /**
32231          * @event rowclick
32232          * Fires when a row is clicked
32233          * @param {Grid} this
32234          * @param {Number} rowIndex
32235          * @param {Roo.EventObject} e
32236          */
32237         "rowclick" : true,
32238         /**
32239          * @event rowdblclick
32240          * Fires when a row is double clicked
32241          * @param {Grid} this
32242          * @param {Number} rowIndex
32243          * @param {Roo.EventObject} e
32244          */
32245         "rowdblclick" : true,
32246         /**
32247          * @event headerclick
32248          * Fires when a header is clicked
32249          * @param {Grid} this
32250          * @param {Number} columnIndex
32251          * @param {Roo.EventObject} e
32252          */
32253         "headerclick" : true,
32254         /**
32255          * @event headerdblclick
32256          * Fires when a header cell is double clicked
32257          * @param {Grid} this
32258          * @param {Number} columnIndex
32259          * @param {Roo.EventObject} e
32260          */
32261         "headerdblclick" : true,
32262         /**
32263          * @event rowcontextmenu
32264          * Fires when a row is right clicked
32265          * @param {Grid} this
32266          * @param {Number} rowIndex
32267          * @param {Roo.EventObject} e
32268          */
32269         "rowcontextmenu" : true,
32270         /**
32271          * @event cellcontextmenu
32272          * Fires when a cell is right clicked
32273          * @param {Grid} this
32274          * @param {Number} rowIndex
32275          * @param {Number} cellIndex
32276          * @param {Roo.EventObject} e
32277          */
32278          "cellcontextmenu" : true,
32279         /**
32280          * @event headercontextmenu
32281          * Fires when a header is right clicked
32282          * @param {Grid} this
32283          * @param {Number} columnIndex
32284          * @param {Roo.EventObject} e
32285          */
32286         "headercontextmenu" : true,
32287         /**
32288          * @event bodyscroll
32289          * Fires when the body element is scrolled
32290          * @param {Number} scrollLeft
32291          * @param {Number} scrollTop
32292          */
32293         "bodyscroll" : true,
32294         /**
32295          * @event columnresize
32296          * Fires when the user resizes a column
32297          * @param {Number} columnIndex
32298          * @param {Number} newSize
32299          */
32300         "columnresize" : true,
32301         /**
32302          * @event columnmove
32303          * Fires when the user moves a column
32304          * @param {Number} oldIndex
32305          * @param {Number} newIndex
32306          */
32307         "columnmove" : true,
32308         /**
32309          * @event startdrag
32310          * Fires when row(s) start being dragged
32311          * @param {Grid} this
32312          * @param {Roo.GridDD} dd The drag drop object
32313          * @param {event} e The raw browser event
32314          */
32315         "startdrag" : true,
32316         /**
32317          * @event enddrag
32318          * Fires when a drag operation is complete
32319          * @param {Grid} this
32320          * @param {Roo.GridDD} dd The drag drop object
32321          * @param {event} e The raw browser event
32322          */
32323         "enddrag" : true,
32324         /**
32325          * @event dragdrop
32326          * Fires when dragged row(s) are dropped on a valid DD target
32327          * @param {Grid} this
32328          * @param {Roo.GridDD} dd The drag drop object
32329          * @param {String} targetId The target drag drop object
32330          * @param {event} e The raw browser event
32331          */
32332         "dragdrop" : true,
32333         /**
32334          * @event dragover
32335          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
32336          * @param {Grid} this
32337          * @param {Roo.GridDD} dd The drag drop object
32338          * @param {String} targetId The target drag drop object
32339          * @param {event} e The raw browser event
32340          */
32341         "dragover" : true,
32342         /**
32343          * @event dragenter
32344          *  Fires when the dragged row(s) first cross another DD target while being dragged
32345          * @param {Grid} this
32346          * @param {Roo.GridDD} dd The drag drop object
32347          * @param {String} targetId The target drag drop object
32348          * @param {event} e The raw browser event
32349          */
32350         "dragenter" : true,
32351         /**
32352          * @event dragout
32353          * Fires when the dragged row(s) leave another DD target while being dragged
32354          * @param {Grid} this
32355          * @param {Roo.GridDD} dd The drag drop object
32356          * @param {String} targetId The target drag drop object
32357          * @param {event} e The raw browser event
32358          */
32359         "dragout" : true,
32360         /**
32361          * @event rowclass
32362          * Fires when a row is rendered, so you can change add a style to it.
32363          * @param {GridView} gridview   The grid view
32364          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
32365          */
32366         'rowclass' : true,
32367
32368         /**
32369          * @event render
32370          * Fires when the grid is rendered
32371          * @param {Grid} grid
32372          */
32373         'render' : true
32374     });
32375
32376     Roo.grid.Grid.superclass.constructor.call(this);
32377 };
32378 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
32379     
32380     /**
32381      * @cfg {String} ddGroup - drag drop group.
32382      */
32383
32384     /**
32385      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
32386      */
32387     minColumnWidth : 25,
32388
32389     /**
32390      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
32391      * <b>on initial render.</b> It is more efficient to explicitly size the columns
32392      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
32393      */
32394     autoSizeColumns : false,
32395
32396     /**
32397      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
32398      */
32399     autoSizeHeaders : true,
32400
32401     /**
32402      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
32403      */
32404     monitorWindowResize : true,
32405
32406     /**
32407      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
32408      * rows measured to get a columns size. Default is 0 (all rows).
32409      */
32410     maxRowsToMeasure : 0,
32411
32412     /**
32413      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
32414      */
32415     trackMouseOver : true,
32416
32417     /**
32418     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
32419     */
32420     
32421     /**
32422     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
32423     */
32424     enableDragDrop : false,
32425     
32426     /**
32427     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
32428     */
32429     enableColumnMove : true,
32430     
32431     /**
32432     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
32433     */
32434     enableColumnHide : true,
32435     
32436     /**
32437     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
32438     */
32439     enableRowHeightSync : false,
32440     
32441     /**
32442     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
32443     */
32444     stripeRows : true,
32445     
32446     /**
32447     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
32448     */
32449     autoHeight : false,
32450
32451     /**
32452      * @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.
32453      */
32454     autoExpandColumn : false,
32455
32456     /**
32457     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
32458     * Default is 50.
32459     */
32460     autoExpandMin : 50,
32461
32462     /**
32463     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
32464     */
32465     autoExpandMax : 1000,
32466
32467     /**
32468     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
32469     */
32470     view : null,
32471
32472     /**
32473     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
32474     */
32475     loadMask : false,
32476     /**
32477     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
32478     */
32479     dropTarget: false,
32480     
32481    
32482     
32483     // private
32484     rendered : false,
32485
32486     /**
32487     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
32488     * of a fixed width. Default is false.
32489     */
32490     /**
32491     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
32492     */
32493     /**
32494      * Called once after all setup has been completed and the grid is ready to be rendered.
32495      * @return {Roo.grid.Grid} this
32496      */
32497     render : function()
32498     {
32499         var c = this.container;
32500         // try to detect autoHeight/width mode
32501         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
32502             this.autoHeight = true;
32503         }
32504         var view = this.getView();
32505         view.init(this);
32506
32507         c.on("click", this.onClick, this);
32508         c.on("dblclick", this.onDblClick, this);
32509         c.on("contextmenu", this.onContextMenu, this);
32510         c.on("keydown", this.onKeyDown, this);
32511         if (Roo.isTouch) {
32512             c.on("touchstart", this.onTouchStart, this);
32513         }
32514
32515         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
32516
32517         this.getSelectionModel().init(this);
32518
32519         view.render();
32520
32521         if(this.loadMask){
32522             this.loadMask = new Roo.LoadMask(this.container,
32523                     Roo.apply({store:this.dataSource}, this.loadMask));
32524         }
32525         
32526         
32527         if (this.toolbar && this.toolbar.xtype) {
32528             this.toolbar.container = this.getView().getHeaderPanel(true);
32529             this.toolbar = new Roo.Toolbar(this.toolbar);
32530         }
32531         if (this.footer && this.footer.xtype) {
32532             this.footer.dataSource = this.getDataSource();
32533             this.footer.container = this.getView().getFooterPanel(true);
32534             this.footer = Roo.factory(this.footer, Roo);
32535         }
32536         if (this.dropTarget && this.dropTarget.xtype) {
32537             delete this.dropTarget.xtype;
32538             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
32539         }
32540         
32541         
32542         this.rendered = true;
32543         this.fireEvent('render', this);
32544         return this;
32545     },
32546
32547     /**
32548      * Reconfigures the grid to use a different Store and Column Model.
32549      * The View will be bound to the new objects and refreshed.
32550      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
32551      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
32552      */
32553     reconfigure : function(dataSource, colModel){
32554         if(this.loadMask){
32555             this.loadMask.destroy();
32556             this.loadMask = new Roo.LoadMask(this.container,
32557                     Roo.apply({store:dataSource}, this.loadMask));
32558         }
32559         this.view.bind(dataSource, colModel);
32560         this.dataSource = dataSource;
32561         this.colModel = colModel;
32562         this.view.refresh(true);
32563     },
32564     /**
32565      * addColumns
32566      * Add's a column, default at the end..
32567      
32568      * @param {int} position to add (default end)
32569      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
32570      */
32571     addColumns : function(pos, ar)
32572     {
32573         
32574         for (var i =0;i< ar.length;i++) {
32575             var cfg = ar[i];
32576             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
32577             this.cm.lookup[cfg.id] = cfg;
32578         }
32579         
32580         
32581         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
32582             pos = this.cm.config.length; //this.cm.config.push(cfg);
32583         } 
32584         pos = Math.max(0,pos);
32585         ar.unshift(0);
32586         ar.unshift(pos);
32587         this.cm.config.splice.apply(this.cm.config, ar);
32588         
32589         
32590         
32591         this.view.generateRules(this.cm);
32592         this.view.refresh(true);
32593         
32594     },
32595     
32596     
32597     
32598     
32599     // private
32600     onKeyDown : function(e){
32601         this.fireEvent("keydown", e);
32602     },
32603
32604     /**
32605      * Destroy this grid.
32606      * @param {Boolean} removeEl True to remove the element
32607      */
32608     destroy : function(removeEl, keepListeners){
32609         if(this.loadMask){
32610             this.loadMask.destroy();
32611         }
32612         var c = this.container;
32613         c.removeAllListeners();
32614         this.view.destroy();
32615         this.colModel.purgeListeners();
32616         if(!keepListeners){
32617             this.purgeListeners();
32618         }
32619         c.update("");
32620         if(removeEl === true){
32621             c.remove();
32622         }
32623     },
32624
32625     // private
32626     processEvent : function(name, e){
32627         // does this fire select???
32628         //Roo.log('grid:processEvent '  + name);
32629         
32630         if (name != 'touchstart' ) {
32631             this.fireEvent(name, e);    
32632         }
32633         
32634         var t = e.getTarget();
32635         var v = this.view;
32636         var header = v.findHeaderIndex(t);
32637         if(header !== false){
32638             var ename = name == 'touchstart' ? 'click' : name;
32639              
32640             this.fireEvent("header" + ename, this, header, e);
32641         }else{
32642             var row = v.findRowIndex(t);
32643             var cell = v.findCellIndex(t);
32644             if (name == 'touchstart') {
32645                 // first touch is always a click.
32646                 // hopefull this happens after selection is updated.?
32647                 name = false;
32648                 
32649                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
32650                     var cs = this.selModel.getSelectedCell();
32651                     if (row == cs[0] && cell == cs[1]){
32652                         name = 'dblclick';
32653                     }
32654                 }
32655                 if (typeof(this.selModel.getSelections) != 'undefined') {
32656                     var cs = this.selModel.getSelections();
32657                     var ds = this.dataSource;
32658                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
32659                         name = 'dblclick';
32660                     }
32661                 }
32662                 if (!name) {
32663                     return;
32664                 }
32665             }
32666             
32667             
32668             if(row !== false){
32669                 this.fireEvent("row" + name, this, row, e);
32670                 if(cell !== false){
32671                     this.fireEvent("cell" + name, this, row, cell, e);
32672                 }
32673             }
32674         }
32675     },
32676
32677     // private
32678     onClick : function(e){
32679         this.processEvent("click", e);
32680     },
32681    // private
32682     onTouchStart : function(e){
32683         this.processEvent("touchstart", e);
32684     },
32685
32686     // private
32687     onContextMenu : function(e, t){
32688         this.processEvent("contextmenu", e);
32689     },
32690
32691     // private
32692     onDblClick : function(e){
32693         this.processEvent("dblclick", e);
32694     },
32695
32696     // private
32697     walkCells : function(row, col, step, fn, scope){
32698         var cm = this.colModel, clen = cm.getColumnCount();
32699         var ds = this.dataSource, rlen = ds.getCount(), first = true;
32700         if(step < 0){
32701             if(col < 0){
32702                 row--;
32703                 first = false;
32704             }
32705             while(row >= 0){
32706                 if(!first){
32707                     col = clen-1;
32708                 }
32709                 first = false;
32710                 while(col >= 0){
32711                     if(fn.call(scope || this, row, col, cm) === true){
32712                         return [row, col];
32713                     }
32714                     col--;
32715                 }
32716                 row--;
32717             }
32718         } else {
32719             if(col >= clen){
32720                 row++;
32721                 first = false;
32722             }
32723             while(row < rlen){
32724                 if(!first){
32725                     col = 0;
32726                 }
32727                 first = false;
32728                 while(col < clen){
32729                     if(fn.call(scope || this, row, col, cm) === true){
32730                         return [row, col];
32731                     }
32732                     col++;
32733                 }
32734                 row++;
32735             }
32736         }
32737         return null;
32738     },
32739
32740     // private
32741     getSelections : function(){
32742         return this.selModel.getSelections();
32743     },
32744
32745     /**
32746      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
32747      * but if manual update is required this method will initiate it.
32748      */
32749     autoSize : function(){
32750         if(this.rendered){
32751             this.view.layout();
32752             if(this.view.adjustForScroll){
32753                 this.view.adjustForScroll();
32754             }
32755         }
32756     },
32757
32758     /**
32759      * Returns the grid's underlying element.
32760      * @return {Element} The element
32761      */
32762     getGridEl : function(){
32763         return this.container;
32764     },
32765
32766     // private for compatibility, overridden by editor grid
32767     stopEditing : function(){},
32768
32769     /**
32770      * Returns the grid's SelectionModel.
32771      * @return {SelectionModel}
32772      */
32773     getSelectionModel : function(){
32774         if(!this.selModel){
32775             this.selModel = new Roo.grid.RowSelectionModel();
32776         }
32777         return this.selModel;
32778     },
32779
32780     /**
32781      * Returns the grid's DataSource.
32782      * @return {DataSource}
32783      */
32784     getDataSource : function(){
32785         return this.dataSource;
32786     },
32787
32788     /**
32789      * Returns the grid's ColumnModel.
32790      * @return {ColumnModel}
32791      */
32792     getColumnModel : function(){
32793         return this.colModel;
32794     },
32795
32796     /**
32797      * Returns the grid's GridView object.
32798      * @return {GridView}
32799      */
32800     getView : function(){
32801         if(!this.view){
32802             this.view = new Roo.grid.GridView(this.viewConfig);
32803         }
32804         return this.view;
32805     },
32806     /**
32807      * Called to get grid's drag proxy text, by default returns this.ddText.
32808      * @return {String}
32809      */
32810     getDragDropText : function(){
32811         var count = this.selModel.getCount();
32812         return String.format(this.ddText, count, count == 1 ? '' : 's');
32813     }
32814 });
32815 /**
32816  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
32817  * %0 is replaced with the number of selected rows.
32818  * @type String
32819  */
32820 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
32821  * Based on:
32822  * Ext JS Library 1.1.1
32823  * Copyright(c) 2006-2007, Ext JS, LLC.
32824  *
32825  * Originally Released Under LGPL - original licence link has changed is not relivant.
32826  *
32827  * Fork - LGPL
32828  * <script type="text/javascript">
32829  */
32830  
32831 Roo.grid.AbstractGridView = function(){
32832         this.grid = null;
32833         
32834         this.events = {
32835             "beforerowremoved" : true,
32836             "beforerowsinserted" : true,
32837             "beforerefresh" : true,
32838             "rowremoved" : true,
32839             "rowsinserted" : true,
32840             "rowupdated" : true,
32841             "refresh" : true
32842         };
32843     Roo.grid.AbstractGridView.superclass.constructor.call(this);
32844 };
32845
32846 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
32847     rowClass : "x-grid-row",
32848     cellClass : "x-grid-cell",
32849     tdClass : "x-grid-td",
32850     hdClass : "x-grid-hd",
32851     splitClass : "x-grid-hd-split",
32852     
32853     init: function(grid){
32854         this.grid = grid;
32855                 var cid = this.grid.getGridEl().id;
32856         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
32857         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
32858         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
32859         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
32860         },
32861         
32862     getColumnRenderers : function(){
32863         var renderers = [];
32864         var cm = this.grid.colModel;
32865         var colCount = cm.getColumnCount();
32866         for(var i = 0; i < colCount; i++){
32867             renderers[i] = cm.getRenderer(i);
32868         }
32869         return renderers;
32870     },
32871     
32872     getColumnIds : function(){
32873         var ids = [];
32874         var cm = this.grid.colModel;
32875         var colCount = cm.getColumnCount();
32876         for(var i = 0; i < colCount; i++){
32877             ids[i] = cm.getColumnId(i);
32878         }
32879         return ids;
32880     },
32881     
32882     getDataIndexes : function(){
32883         if(!this.indexMap){
32884             this.indexMap = this.buildIndexMap();
32885         }
32886         return this.indexMap.colToData;
32887     },
32888     
32889     getColumnIndexByDataIndex : function(dataIndex){
32890         if(!this.indexMap){
32891             this.indexMap = this.buildIndexMap();
32892         }
32893         return this.indexMap.dataToCol[dataIndex];
32894     },
32895     
32896     /**
32897      * Set a css style for a column dynamically. 
32898      * @param {Number} colIndex The index of the column
32899      * @param {String} name The css property name
32900      * @param {String} value The css value
32901      */
32902     setCSSStyle : function(colIndex, name, value){
32903         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
32904         Roo.util.CSS.updateRule(selector, name, value);
32905     },
32906     
32907     generateRules : function(cm){
32908         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
32909         Roo.util.CSS.removeStyleSheet(rulesId);
32910         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
32911             var cid = cm.getColumnId(i);
32912             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
32913                          this.tdSelector, cid, " {\n}\n",
32914                          this.hdSelector, cid, " {\n}\n",
32915                          this.splitSelector, cid, " {\n}\n");
32916         }
32917         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
32918     }
32919 });/*
32920  * Based on:
32921  * Ext JS Library 1.1.1
32922  * Copyright(c) 2006-2007, Ext JS, LLC.
32923  *
32924  * Originally Released Under LGPL - original licence link has changed is not relivant.
32925  *
32926  * Fork - LGPL
32927  * <script type="text/javascript">
32928  */
32929
32930 // private
32931 // This is a support class used internally by the Grid components
32932 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
32933     this.grid = grid;
32934     this.view = grid.getView();
32935     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
32936     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
32937     if(hd2){
32938         this.setHandleElId(Roo.id(hd));
32939         this.setOuterHandleElId(Roo.id(hd2));
32940     }
32941     this.scroll = false;
32942 };
32943 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
32944     maxDragWidth: 120,
32945     getDragData : function(e){
32946         var t = Roo.lib.Event.getTarget(e);
32947         var h = this.view.findHeaderCell(t);
32948         if(h){
32949             return {ddel: h.firstChild, header:h};
32950         }
32951         return false;
32952     },
32953
32954     onInitDrag : function(e){
32955         this.view.headersDisabled = true;
32956         var clone = this.dragData.ddel.cloneNode(true);
32957         clone.id = Roo.id();
32958         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
32959         this.proxy.update(clone);
32960         return true;
32961     },
32962
32963     afterValidDrop : function(){
32964         var v = this.view;
32965         setTimeout(function(){
32966             v.headersDisabled = false;
32967         }, 50);
32968     },
32969
32970     afterInvalidDrop : function(){
32971         var v = this.view;
32972         setTimeout(function(){
32973             v.headersDisabled = false;
32974         }, 50);
32975     }
32976 });
32977 /*
32978  * Based on:
32979  * Ext JS Library 1.1.1
32980  * Copyright(c) 2006-2007, Ext JS, LLC.
32981  *
32982  * Originally Released Under LGPL - original licence link has changed is not relivant.
32983  *
32984  * Fork - LGPL
32985  * <script type="text/javascript">
32986  */
32987 // private
32988 // This is a support class used internally by the Grid components
32989 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
32990     this.grid = grid;
32991     this.view = grid.getView();
32992     // split the proxies so they don't interfere with mouse events
32993     this.proxyTop = Roo.DomHelper.append(document.body, {
32994         cls:"col-move-top", html:"&#160;"
32995     }, true);
32996     this.proxyBottom = Roo.DomHelper.append(document.body, {
32997         cls:"col-move-bottom", html:"&#160;"
32998     }, true);
32999     this.proxyTop.hide = this.proxyBottom.hide = function(){
33000         this.setLeftTop(-100,-100);
33001         this.setStyle("visibility", "hidden");
33002     };
33003     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
33004     // temporarily disabled
33005     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
33006     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
33007 };
33008 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
33009     proxyOffsets : [-4, -9],
33010     fly: Roo.Element.fly,
33011
33012     getTargetFromEvent : function(e){
33013         var t = Roo.lib.Event.getTarget(e);
33014         var cindex = this.view.findCellIndex(t);
33015         if(cindex !== false){
33016             return this.view.getHeaderCell(cindex);
33017         }
33018         return null;
33019     },
33020
33021     nextVisible : function(h){
33022         var v = this.view, cm = this.grid.colModel;
33023         h = h.nextSibling;
33024         while(h){
33025             if(!cm.isHidden(v.getCellIndex(h))){
33026                 return h;
33027             }
33028             h = h.nextSibling;
33029         }
33030         return null;
33031     },
33032
33033     prevVisible : function(h){
33034         var v = this.view, cm = this.grid.colModel;
33035         h = h.prevSibling;
33036         while(h){
33037             if(!cm.isHidden(v.getCellIndex(h))){
33038                 return h;
33039             }
33040             h = h.prevSibling;
33041         }
33042         return null;
33043     },
33044
33045     positionIndicator : function(h, n, e){
33046         var x = Roo.lib.Event.getPageX(e);
33047         var r = Roo.lib.Dom.getRegion(n.firstChild);
33048         var px, pt, py = r.top + this.proxyOffsets[1];
33049         if((r.right - x) <= (r.right-r.left)/2){
33050             px = r.right+this.view.borderWidth;
33051             pt = "after";
33052         }else{
33053             px = r.left;
33054             pt = "before";
33055         }
33056         var oldIndex = this.view.getCellIndex(h);
33057         var newIndex = this.view.getCellIndex(n);
33058
33059         if(this.grid.colModel.isFixed(newIndex)){
33060             return false;
33061         }
33062
33063         var locked = this.grid.colModel.isLocked(newIndex);
33064
33065         if(pt == "after"){
33066             newIndex++;
33067         }
33068         if(oldIndex < newIndex){
33069             newIndex--;
33070         }
33071         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
33072             return false;
33073         }
33074         px +=  this.proxyOffsets[0];
33075         this.proxyTop.setLeftTop(px, py);
33076         this.proxyTop.show();
33077         if(!this.bottomOffset){
33078             this.bottomOffset = this.view.mainHd.getHeight();
33079         }
33080         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
33081         this.proxyBottom.show();
33082         return pt;
33083     },
33084
33085     onNodeEnter : function(n, dd, e, data){
33086         if(data.header != n){
33087             this.positionIndicator(data.header, n, e);
33088         }
33089     },
33090
33091     onNodeOver : function(n, dd, e, data){
33092         var result = false;
33093         if(data.header != n){
33094             result = this.positionIndicator(data.header, n, e);
33095         }
33096         if(!result){
33097             this.proxyTop.hide();
33098             this.proxyBottom.hide();
33099         }
33100         return result ? this.dropAllowed : this.dropNotAllowed;
33101     },
33102
33103     onNodeOut : function(n, dd, e, data){
33104         this.proxyTop.hide();
33105         this.proxyBottom.hide();
33106     },
33107
33108     onNodeDrop : function(n, dd, e, data){
33109         var h = data.header;
33110         if(h != n){
33111             var cm = this.grid.colModel;
33112             var x = Roo.lib.Event.getPageX(e);
33113             var r = Roo.lib.Dom.getRegion(n.firstChild);
33114             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
33115             var oldIndex = this.view.getCellIndex(h);
33116             var newIndex = this.view.getCellIndex(n);
33117             var locked = cm.isLocked(newIndex);
33118             if(pt == "after"){
33119                 newIndex++;
33120             }
33121             if(oldIndex < newIndex){
33122                 newIndex--;
33123             }
33124             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
33125                 return false;
33126             }
33127             cm.setLocked(oldIndex, locked, true);
33128             cm.moveColumn(oldIndex, newIndex);
33129             this.grid.fireEvent("columnmove", oldIndex, newIndex);
33130             return true;
33131         }
33132         return false;
33133     }
33134 });
33135 /*
33136  * Based on:
33137  * Ext JS Library 1.1.1
33138  * Copyright(c) 2006-2007, Ext JS, LLC.
33139  *
33140  * Originally Released Under LGPL - original licence link has changed is not relivant.
33141  *
33142  * Fork - LGPL
33143  * <script type="text/javascript">
33144  */
33145   
33146 /**
33147  * @class Roo.grid.GridView
33148  * @extends Roo.util.Observable
33149  *
33150  * @constructor
33151  * @param {Object} config
33152  */
33153 Roo.grid.GridView = function(config){
33154     Roo.grid.GridView.superclass.constructor.call(this);
33155     this.el = null;
33156
33157     Roo.apply(this, config);
33158 };
33159
33160 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
33161
33162     unselectable :  'unselectable="on"',
33163     unselectableCls :  'x-unselectable',
33164     
33165     
33166     rowClass : "x-grid-row",
33167
33168     cellClass : "x-grid-col",
33169
33170     tdClass : "x-grid-td",
33171
33172     hdClass : "x-grid-hd",
33173
33174     splitClass : "x-grid-split",
33175
33176     sortClasses : ["sort-asc", "sort-desc"],
33177
33178     enableMoveAnim : false,
33179
33180     hlColor: "C3DAF9",
33181
33182     dh : Roo.DomHelper,
33183
33184     fly : Roo.Element.fly,
33185
33186     css : Roo.util.CSS,
33187
33188     borderWidth: 1,
33189
33190     splitOffset: 3,
33191
33192     scrollIncrement : 22,
33193
33194     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
33195
33196     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
33197
33198     bind : function(ds, cm){
33199         if(this.ds){
33200             this.ds.un("load", this.onLoad, this);
33201             this.ds.un("datachanged", this.onDataChange, this);
33202             this.ds.un("add", this.onAdd, this);
33203             this.ds.un("remove", this.onRemove, this);
33204             this.ds.un("update", this.onUpdate, this);
33205             this.ds.un("clear", this.onClear, this);
33206         }
33207         if(ds){
33208             ds.on("load", this.onLoad, this);
33209             ds.on("datachanged", this.onDataChange, this);
33210             ds.on("add", this.onAdd, this);
33211             ds.on("remove", this.onRemove, this);
33212             ds.on("update", this.onUpdate, this);
33213             ds.on("clear", this.onClear, this);
33214         }
33215         this.ds = ds;
33216
33217         if(this.cm){
33218             this.cm.un("widthchange", this.onColWidthChange, this);
33219             this.cm.un("headerchange", this.onHeaderChange, this);
33220             this.cm.un("hiddenchange", this.onHiddenChange, this);
33221             this.cm.un("columnmoved", this.onColumnMove, this);
33222             this.cm.un("columnlockchange", this.onColumnLock, this);
33223         }
33224         if(cm){
33225             this.generateRules(cm);
33226             cm.on("widthchange", this.onColWidthChange, this);
33227             cm.on("headerchange", this.onHeaderChange, this);
33228             cm.on("hiddenchange", this.onHiddenChange, this);
33229             cm.on("columnmoved", this.onColumnMove, this);
33230             cm.on("columnlockchange", this.onColumnLock, this);
33231         }
33232         this.cm = cm;
33233     },
33234
33235     init: function(grid){
33236         Roo.grid.GridView.superclass.init.call(this, grid);
33237
33238         this.bind(grid.dataSource, grid.colModel);
33239
33240         grid.on("headerclick", this.handleHeaderClick, this);
33241
33242         if(grid.trackMouseOver){
33243             grid.on("mouseover", this.onRowOver, this);
33244             grid.on("mouseout", this.onRowOut, this);
33245         }
33246         grid.cancelTextSelection = function(){};
33247         this.gridId = grid.id;
33248
33249         var tpls = this.templates || {};
33250
33251         if(!tpls.master){
33252             tpls.master = new Roo.Template(
33253                '<div class="x-grid" hidefocus="true">',
33254                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
33255                   '<div class="x-grid-topbar"></div>',
33256                   '<div class="x-grid-scroller"><div></div></div>',
33257                   '<div class="x-grid-locked">',
33258                       '<div class="x-grid-header">{lockedHeader}</div>',
33259                       '<div class="x-grid-body">{lockedBody}</div>',
33260                   "</div>",
33261                   '<div class="x-grid-viewport">',
33262                       '<div class="x-grid-header">{header}</div>',
33263                       '<div class="x-grid-body">{body}</div>',
33264                   "</div>",
33265                   '<div class="x-grid-bottombar"></div>',
33266                  
33267                   '<div class="x-grid-resize-proxy">&#160;</div>',
33268                "</div>"
33269             );
33270             tpls.master.disableformats = true;
33271         }
33272
33273         if(!tpls.header){
33274             tpls.header = new Roo.Template(
33275                '<table border="0" cellspacing="0" cellpadding="0">',
33276                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
33277                "</table>{splits}"
33278             );
33279             tpls.header.disableformats = true;
33280         }
33281         tpls.header.compile();
33282
33283         if(!tpls.hcell){
33284             tpls.hcell = new Roo.Template(
33285                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
33286                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
33287                 "</div></td>"
33288              );
33289              tpls.hcell.disableFormats = true;
33290         }
33291         tpls.hcell.compile();
33292
33293         if(!tpls.hsplit){
33294             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
33295                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
33296             tpls.hsplit.disableFormats = true;
33297         }
33298         tpls.hsplit.compile();
33299
33300         if(!tpls.body){
33301             tpls.body = new Roo.Template(
33302                '<table border="0" cellspacing="0" cellpadding="0">',
33303                "<tbody>{rows}</tbody>",
33304                "</table>"
33305             );
33306             tpls.body.disableFormats = true;
33307         }
33308         tpls.body.compile();
33309
33310         if(!tpls.row){
33311             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
33312             tpls.row.disableFormats = true;
33313         }
33314         tpls.row.compile();
33315
33316         if(!tpls.cell){
33317             tpls.cell = new Roo.Template(
33318                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
33319                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
33320                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
33321                 "</td>"
33322             );
33323             tpls.cell.disableFormats = true;
33324         }
33325         tpls.cell.compile();
33326
33327         this.templates = tpls;
33328     },
33329
33330     // remap these for backwards compat
33331     onColWidthChange : function(){
33332         this.updateColumns.apply(this, arguments);
33333     },
33334     onHeaderChange : function(){
33335         this.updateHeaders.apply(this, arguments);
33336     }, 
33337     onHiddenChange : function(){
33338         this.handleHiddenChange.apply(this, arguments);
33339     },
33340     onColumnMove : function(){
33341         this.handleColumnMove.apply(this, arguments);
33342     },
33343     onColumnLock : function(){
33344         this.handleLockChange.apply(this, arguments);
33345     },
33346
33347     onDataChange : function(){
33348         this.refresh();
33349         this.updateHeaderSortState();
33350     },
33351
33352     onClear : function(){
33353         this.refresh();
33354     },
33355
33356     onUpdate : function(ds, record){
33357         this.refreshRow(record);
33358     },
33359
33360     refreshRow : function(record){
33361         var ds = this.ds, index;
33362         if(typeof record == 'number'){
33363             index = record;
33364             record = ds.getAt(index);
33365         }else{
33366             index = ds.indexOf(record);
33367         }
33368         this.insertRows(ds, index, index, true);
33369         this.onRemove(ds, record, index+1, true);
33370         this.syncRowHeights(index, index);
33371         this.layout();
33372         this.fireEvent("rowupdated", this, index, record);
33373     },
33374
33375     onAdd : function(ds, records, index){
33376         this.insertRows(ds, index, index + (records.length-1));
33377     },
33378
33379     onRemove : function(ds, record, index, isUpdate){
33380         if(isUpdate !== true){
33381             this.fireEvent("beforerowremoved", this, index, record);
33382         }
33383         var bt = this.getBodyTable(), lt = this.getLockedTable();
33384         if(bt.rows[index]){
33385             bt.firstChild.removeChild(bt.rows[index]);
33386         }
33387         if(lt.rows[index]){
33388             lt.firstChild.removeChild(lt.rows[index]);
33389         }
33390         if(isUpdate !== true){
33391             this.stripeRows(index);
33392             this.syncRowHeights(index, index);
33393             this.layout();
33394             this.fireEvent("rowremoved", this, index, record);
33395         }
33396     },
33397
33398     onLoad : function(){
33399         this.scrollToTop();
33400     },
33401
33402     /**
33403      * Scrolls the grid to the top
33404      */
33405     scrollToTop : function(){
33406         if(this.scroller){
33407             this.scroller.dom.scrollTop = 0;
33408             this.syncScroll();
33409         }
33410     },
33411
33412     /**
33413      * Gets a panel in the header of the grid that can be used for toolbars etc.
33414      * After modifying the contents of this panel a call to grid.autoSize() may be
33415      * required to register any changes in size.
33416      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
33417      * @return Roo.Element
33418      */
33419     getHeaderPanel : function(doShow){
33420         if(doShow){
33421             this.headerPanel.show();
33422         }
33423         return this.headerPanel;
33424     },
33425
33426     /**
33427      * Gets a panel in the footer of the grid that can be used for toolbars etc.
33428      * After modifying the contents of this panel a call to grid.autoSize() may be
33429      * required to register any changes in size.
33430      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
33431      * @return Roo.Element
33432      */
33433     getFooterPanel : function(doShow){
33434         if(doShow){
33435             this.footerPanel.show();
33436         }
33437         return this.footerPanel;
33438     },
33439
33440     initElements : function(){
33441         var E = Roo.Element;
33442         var el = this.grid.getGridEl().dom.firstChild;
33443         var cs = el.childNodes;
33444
33445         this.el = new E(el);
33446         
33447          this.focusEl = new E(el.firstChild);
33448         this.focusEl.swallowEvent("click", true);
33449         
33450         this.headerPanel = new E(cs[1]);
33451         this.headerPanel.enableDisplayMode("block");
33452
33453         this.scroller = new E(cs[2]);
33454         this.scrollSizer = new E(this.scroller.dom.firstChild);
33455
33456         this.lockedWrap = new E(cs[3]);
33457         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
33458         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
33459
33460         this.mainWrap = new E(cs[4]);
33461         this.mainHd = new E(this.mainWrap.dom.firstChild);
33462         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
33463
33464         this.footerPanel = new E(cs[5]);
33465         this.footerPanel.enableDisplayMode("block");
33466
33467         this.resizeProxy = new E(cs[6]);
33468
33469         this.headerSelector = String.format(
33470            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
33471            this.lockedHd.id, this.mainHd.id
33472         );
33473
33474         this.splitterSelector = String.format(
33475            '#{0} div.x-grid-split, #{1} div.x-grid-split',
33476            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
33477         );
33478     },
33479     idToCssName : function(s)
33480     {
33481         return s.replace(/[^a-z0-9]+/ig, '-');
33482     },
33483
33484     getHeaderCell : function(index){
33485         return Roo.DomQuery.select(this.headerSelector)[index];
33486     },
33487
33488     getHeaderCellMeasure : function(index){
33489         return this.getHeaderCell(index).firstChild;
33490     },
33491
33492     getHeaderCellText : function(index){
33493         return this.getHeaderCell(index).firstChild.firstChild;
33494     },
33495
33496     getLockedTable : function(){
33497         return this.lockedBody.dom.firstChild;
33498     },
33499
33500     getBodyTable : function(){
33501         return this.mainBody.dom.firstChild;
33502     },
33503
33504     getLockedRow : function(index){
33505         return this.getLockedTable().rows[index];
33506     },
33507
33508     getRow : function(index){
33509         return this.getBodyTable().rows[index];
33510     },
33511
33512     getRowComposite : function(index){
33513         if(!this.rowEl){
33514             this.rowEl = new Roo.CompositeElementLite();
33515         }
33516         var els = [], lrow, mrow;
33517         if(lrow = this.getLockedRow(index)){
33518             els.push(lrow);
33519         }
33520         if(mrow = this.getRow(index)){
33521             els.push(mrow);
33522         }
33523         this.rowEl.elements = els;
33524         return this.rowEl;
33525     },
33526     /**
33527      * Gets the 'td' of the cell
33528      * 
33529      * @param {Integer} rowIndex row to select
33530      * @param {Integer} colIndex column to select
33531      * 
33532      * @return {Object} 
33533      */
33534     getCell : function(rowIndex, colIndex){
33535         var locked = this.cm.getLockedCount();
33536         var source;
33537         if(colIndex < locked){
33538             source = this.lockedBody.dom.firstChild;
33539         }else{
33540             source = this.mainBody.dom.firstChild;
33541             colIndex -= locked;
33542         }
33543         return source.rows[rowIndex].childNodes[colIndex];
33544     },
33545
33546     getCellText : function(rowIndex, colIndex){
33547         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
33548     },
33549
33550     getCellBox : function(cell){
33551         var b = this.fly(cell).getBox();
33552         if(Roo.isOpera){ // opera fails to report the Y
33553             b.y = cell.offsetTop + this.mainBody.getY();
33554         }
33555         return b;
33556     },
33557
33558     getCellIndex : function(cell){
33559         var id = String(cell.className).match(this.cellRE);
33560         if(id){
33561             return parseInt(id[1], 10);
33562         }
33563         return 0;
33564     },
33565
33566     findHeaderIndex : function(n){
33567         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33568         return r ? this.getCellIndex(r) : false;
33569     },
33570
33571     findHeaderCell : function(n){
33572         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
33573         return r ? r : false;
33574     },
33575
33576     findRowIndex : function(n){
33577         if(!n){
33578             return false;
33579         }
33580         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
33581         return r ? r.rowIndex : false;
33582     },
33583
33584     findCellIndex : function(node){
33585         var stop = this.el.dom;
33586         while(node && node != stop){
33587             if(this.findRE.test(node.className)){
33588                 return this.getCellIndex(node);
33589             }
33590             node = node.parentNode;
33591         }
33592         return false;
33593     },
33594
33595     getColumnId : function(index){
33596         return this.cm.getColumnId(index);
33597     },
33598
33599     getSplitters : function()
33600     {
33601         if(this.splitterSelector){
33602            return Roo.DomQuery.select(this.splitterSelector);
33603         }else{
33604             return null;
33605       }
33606     },
33607
33608     getSplitter : function(index){
33609         return this.getSplitters()[index];
33610     },
33611
33612     onRowOver : function(e, t){
33613         var row;
33614         if((row = this.findRowIndex(t)) !== false){
33615             this.getRowComposite(row).addClass("x-grid-row-over");
33616         }
33617     },
33618
33619     onRowOut : function(e, t){
33620         var row;
33621         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
33622             this.getRowComposite(row).removeClass("x-grid-row-over");
33623         }
33624     },
33625
33626     renderHeaders : function(){
33627         var cm = this.cm;
33628         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
33629         var cb = [], lb = [], sb = [], lsb = [], p = {};
33630         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33631             p.cellId = "x-grid-hd-0-" + i;
33632             p.splitId = "x-grid-csplit-0-" + i;
33633             p.id = cm.getColumnId(i);
33634             p.value = cm.getColumnHeader(i) || "";
33635             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
33636             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
33637             if(!cm.isLocked(i)){
33638                 cb[cb.length] = ct.apply(p);
33639                 sb[sb.length] = st.apply(p);
33640             }else{
33641                 lb[lb.length] = ct.apply(p);
33642                 lsb[lsb.length] = st.apply(p);
33643             }
33644         }
33645         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
33646                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
33647     },
33648
33649     updateHeaders : function(){
33650         var html = this.renderHeaders();
33651         this.lockedHd.update(html[0]);
33652         this.mainHd.update(html[1]);
33653     },
33654
33655     /**
33656      * Focuses the specified row.
33657      * @param {Number} row The row index
33658      */
33659     focusRow : function(row)
33660     {
33661         //Roo.log('GridView.focusRow');
33662         var x = this.scroller.dom.scrollLeft;
33663         this.focusCell(row, 0, false);
33664         this.scroller.dom.scrollLeft = x;
33665     },
33666
33667     /**
33668      * Focuses the specified cell.
33669      * @param {Number} row The row index
33670      * @param {Number} col The column index
33671      * @param {Boolean} hscroll false to disable horizontal scrolling
33672      */
33673     focusCell : function(row, col, hscroll)
33674     {
33675         //Roo.log('GridView.focusCell');
33676         var el = this.ensureVisible(row, col, hscroll);
33677         this.focusEl.alignTo(el, "tl-tl");
33678         if(Roo.isGecko){
33679             this.focusEl.focus();
33680         }else{
33681             this.focusEl.focus.defer(1, this.focusEl);
33682         }
33683     },
33684
33685     /**
33686      * Scrolls the specified cell into view
33687      * @param {Number} row The row index
33688      * @param {Number} col The column index
33689      * @param {Boolean} hscroll false to disable horizontal scrolling
33690      */
33691     ensureVisible : function(row, col, hscroll)
33692     {
33693         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
33694         //return null; //disable for testing.
33695         if(typeof row != "number"){
33696             row = row.rowIndex;
33697         }
33698         if(row < 0 && row >= this.ds.getCount()){
33699             return  null;
33700         }
33701         col = (col !== undefined ? col : 0);
33702         var cm = this.grid.colModel;
33703         while(cm.isHidden(col)){
33704             col++;
33705         }
33706
33707         var el = this.getCell(row, col);
33708         if(!el){
33709             return null;
33710         }
33711         var c = this.scroller.dom;
33712
33713         var ctop = parseInt(el.offsetTop, 10);
33714         var cleft = parseInt(el.offsetLeft, 10);
33715         var cbot = ctop + el.offsetHeight;
33716         var cright = cleft + el.offsetWidth;
33717         
33718         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
33719         var stop = parseInt(c.scrollTop, 10);
33720         var sleft = parseInt(c.scrollLeft, 10);
33721         var sbot = stop + ch;
33722         var sright = sleft + c.clientWidth;
33723         /*
33724         Roo.log('GridView.ensureVisible:' +
33725                 ' ctop:' + ctop +
33726                 ' c.clientHeight:' + c.clientHeight +
33727                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
33728                 ' stop:' + stop +
33729                 ' cbot:' + cbot +
33730                 ' sbot:' + sbot +
33731                 ' ch:' + ch  
33732                 );
33733         */
33734         if(ctop < stop){
33735              c.scrollTop = ctop;
33736             //Roo.log("set scrolltop to ctop DISABLE?");
33737         }else if(cbot > sbot){
33738             //Roo.log("set scrolltop to cbot-ch");
33739             c.scrollTop = cbot-ch;
33740         }
33741         
33742         if(hscroll !== false){
33743             if(cleft < sleft){
33744                 c.scrollLeft = cleft;
33745             }else if(cright > sright){
33746                 c.scrollLeft = cright-c.clientWidth;
33747             }
33748         }
33749          
33750         return el;
33751     },
33752
33753     updateColumns : function(){
33754         this.grid.stopEditing();
33755         var cm = this.grid.colModel, colIds = this.getColumnIds();
33756         //var totalWidth = cm.getTotalWidth();
33757         var pos = 0;
33758         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33759             //if(cm.isHidden(i)) continue;
33760             var w = cm.getColumnWidth(i);
33761             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33762             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
33763         }
33764         this.updateSplitters();
33765     },
33766
33767     generateRules : function(cm){
33768         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
33769         Roo.util.CSS.removeStyleSheet(rulesId);
33770         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33771             var cid = cm.getColumnId(i);
33772             var align = '';
33773             if(cm.config[i].align){
33774                 align = 'text-align:'+cm.config[i].align+';';
33775             }
33776             var hidden = '';
33777             if(cm.isHidden(i)){
33778                 hidden = 'display:none;';
33779             }
33780             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
33781             ruleBuf.push(
33782                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
33783                     this.hdSelector, cid, " {\n", align, width, "}\n",
33784                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
33785                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
33786         }
33787         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
33788     },
33789
33790     updateSplitters : function(){
33791         var cm = this.cm, s = this.getSplitters();
33792         if(s){ // splitters not created yet
33793             var pos = 0, locked = true;
33794             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
33795                 if(cm.isHidden(i)) {
33796                     continue;
33797                 }
33798                 var w = cm.getColumnWidth(i); // make sure it's a number
33799                 if(!cm.isLocked(i) && locked){
33800                     pos = 0;
33801                     locked = false;
33802                 }
33803                 pos += w;
33804                 s[i].style.left = (pos-this.splitOffset) + "px";
33805             }
33806         }
33807     },
33808
33809     handleHiddenChange : function(colModel, colIndex, hidden){
33810         if(hidden){
33811             this.hideColumn(colIndex);
33812         }else{
33813             this.unhideColumn(colIndex);
33814         }
33815     },
33816
33817     hideColumn : function(colIndex){
33818         var cid = this.getColumnId(colIndex);
33819         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
33820         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
33821         if(Roo.isSafari){
33822             this.updateHeaders();
33823         }
33824         this.updateSplitters();
33825         this.layout();
33826     },
33827
33828     unhideColumn : function(colIndex){
33829         var cid = this.getColumnId(colIndex);
33830         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
33831         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
33832
33833         if(Roo.isSafari){
33834             this.updateHeaders();
33835         }
33836         this.updateSplitters();
33837         this.layout();
33838     },
33839
33840     insertRows : function(dm, firstRow, lastRow, isUpdate){
33841         if(firstRow == 0 && lastRow == dm.getCount()-1){
33842             this.refresh();
33843         }else{
33844             if(!isUpdate){
33845                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
33846             }
33847             var s = this.getScrollState();
33848             var markup = this.renderRows(firstRow, lastRow);
33849             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
33850             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
33851             this.restoreScroll(s);
33852             if(!isUpdate){
33853                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
33854                 this.syncRowHeights(firstRow, lastRow);
33855                 this.stripeRows(firstRow);
33856                 this.layout();
33857             }
33858         }
33859     },
33860
33861     bufferRows : function(markup, target, index){
33862         var before = null, trows = target.rows, tbody = target.tBodies[0];
33863         if(index < trows.length){
33864             before = trows[index];
33865         }
33866         var b = document.createElement("div");
33867         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
33868         var rows = b.firstChild.rows;
33869         for(var i = 0, len = rows.length; i < len; i++){
33870             if(before){
33871                 tbody.insertBefore(rows[0], before);
33872             }else{
33873                 tbody.appendChild(rows[0]);
33874             }
33875         }
33876         b.innerHTML = "";
33877         b = null;
33878     },
33879
33880     deleteRows : function(dm, firstRow, lastRow){
33881         if(dm.getRowCount()<1){
33882             this.fireEvent("beforerefresh", this);
33883             this.mainBody.update("");
33884             this.lockedBody.update("");
33885             this.fireEvent("refresh", this);
33886         }else{
33887             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
33888             var bt = this.getBodyTable();
33889             var tbody = bt.firstChild;
33890             var rows = bt.rows;
33891             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
33892                 tbody.removeChild(rows[firstRow]);
33893             }
33894             this.stripeRows(firstRow);
33895             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
33896         }
33897     },
33898
33899     updateRows : function(dataSource, firstRow, lastRow){
33900         var s = this.getScrollState();
33901         this.refresh();
33902         this.restoreScroll(s);
33903     },
33904
33905     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
33906         if(!noRefresh){
33907            this.refresh();
33908         }
33909         this.updateHeaderSortState();
33910     },
33911
33912     getScrollState : function(){
33913         
33914         var sb = this.scroller.dom;
33915         return {left: sb.scrollLeft, top: sb.scrollTop};
33916     },
33917
33918     stripeRows : function(startRow){
33919         if(!this.grid.stripeRows || this.ds.getCount() < 1){
33920             return;
33921         }
33922         startRow = startRow || 0;
33923         var rows = this.getBodyTable().rows;
33924         var lrows = this.getLockedTable().rows;
33925         var cls = ' x-grid-row-alt ';
33926         for(var i = startRow, len = rows.length; i < len; i++){
33927             var row = rows[i], lrow = lrows[i];
33928             var isAlt = ((i+1) % 2 == 0);
33929             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
33930             if(isAlt == hasAlt){
33931                 continue;
33932             }
33933             if(isAlt){
33934                 row.className += " x-grid-row-alt";
33935             }else{
33936                 row.className = row.className.replace("x-grid-row-alt", "");
33937             }
33938             if(lrow){
33939                 lrow.className = row.className;
33940             }
33941         }
33942     },
33943
33944     restoreScroll : function(state){
33945         //Roo.log('GridView.restoreScroll');
33946         var sb = this.scroller.dom;
33947         sb.scrollLeft = state.left;
33948         sb.scrollTop = state.top;
33949         this.syncScroll();
33950     },
33951
33952     syncScroll : function(){
33953         //Roo.log('GridView.syncScroll');
33954         var sb = this.scroller.dom;
33955         var sh = this.mainHd.dom;
33956         var bs = this.mainBody.dom;
33957         var lv = this.lockedBody.dom;
33958         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
33959         lv.scrollTop = bs.scrollTop = sb.scrollTop;
33960     },
33961
33962     handleScroll : function(e){
33963         this.syncScroll();
33964         var sb = this.scroller.dom;
33965         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
33966         e.stopEvent();
33967     },
33968
33969     handleWheel : function(e){
33970         var d = e.getWheelDelta();
33971         this.scroller.dom.scrollTop -= d*22;
33972         // set this here to prevent jumpy scrolling on large tables
33973         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
33974         e.stopEvent();
33975     },
33976
33977     renderRows : function(startRow, endRow){
33978         // pull in all the crap needed to render rows
33979         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
33980         var colCount = cm.getColumnCount();
33981
33982         if(ds.getCount() < 1){
33983             return ["", ""];
33984         }
33985
33986         // build a map for all the columns
33987         var cs = [];
33988         for(var i = 0; i < colCount; i++){
33989             var name = cm.getDataIndex(i);
33990             cs[i] = {
33991                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
33992                 renderer : cm.getRenderer(i),
33993                 id : cm.getColumnId(i),
33994                 locked : cm.isLocked(i),
33995                 has_editor : cm.isCellEditable(i)
33996             };
33997         }
33998
33999         startRow = startRow || 0;
34000         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
34001
34002         // records to render
34003         var rs = ds.getRange(startRow, endRow);
34004
34005         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
34006     },
34007
34008     // As much as I hate to duplicate code, this was branched because FireFox really hates
34009     // [].join("") on strings. The performance difference was substantial enough to
34010     // branch this function
34011     doRender : Roo.isGecko ?
34012             function(cs, rs, ds, startRow, colCount, stripe){
34013                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34014                 // buffers
34015                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34016                 
34017                 var hasListener = this.grid.hasListener('rowclass');
34018                 var rowcfg = {};
34019                 for(var j = 0, len = rs.length; j < len; j++){
34020                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
34021                     for(var i = 0; i < colCount; i++){
34022                         c = cs[i];
34023                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34024                         p.id = c.id;
34025                         p.css = p.attr = "";
34026                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34027                         if(p.value == undefined || p.value === "") {
34028                             p.value = "&#160;";
34029                         }
34030                         if(c.has_editor){
34031                             p.css += ' x-grid-editable-cell';
34032                         }
34033                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
34034                             p.css +=  ' x-grid-dirty-cell';
34035                         }
34036                         var markup = ct.apply(p);
34037                         if(!c.locked){
34038                             cb+= markup;
34039                         }else{
34040                             lcb+= markup;
34041                         }
34042                     }
34043                     var alt = [];
34044                     if(stripe && ((rowIndex+1) % 2 == 0)){
34045                         alt.push("x-grid-row-alt")
34046                     }
34047                     if(r.dirty){
34048                         alt.push(  " x-grid-dirty-row");
34049                     }
34050                     rp.cells = lcb;
34051                     if(this.getRowClass){
34052                         alt.push(this.getRowClass(r, rowIndex));
34053                     }
34054                     if (hasListener) {
34055                         rowcfg = {
34056                              
34057                             record: r,
34058                             rowIndex : rowIndex,
34059                             rowClass : ''
34060                         };
34061                         this.grid.fireEvent('rowclass', this, rowcfg);
34062                         alt.push(rowcfg.rowClass);
34063                     }
34064                     rp.alt = alt.join(" ");
34065                     lbuf+= rt.apply(rp);
34066                     rp.cells = cb;
34067                     buf+=  rt.apply(rp);
34068                 }
34069                 return [lbuf, buf];
34070             } :
34071             function(cs, rs, ds, startRow, colCount, stripe){
34072                 var ts = this.templates, ct = ts.cell, rt = ts.row;
34073                 // buffers
34074                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
34075                 var hasListener = this.grid.hasListener('rowclass');
34076  
34077                 var rowcfg = {};
34078                 for(var j = 0, len = rs.length; j < len; j++){
34079                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
34080                     for(var i = 0; i < colCount; i++){
34081                         c = cs[i];
34082                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
34083                         p.id = c.id;
34084                         p.css = p.attr = "";
34085                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
34086                         if(p.value == undefined || p.value === "") {
34087                             p.value = "&#160;";
34088                         }
34089                         //Roo.log(c);
34090                          if(c.has_editor){
34091                             p.css += ' x-grid-editable-cell';
34092                         }
34093                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
34094                             p.css += ' x-grid-dirty-cell' 
34095                         }
34096                         
34097                         var markup = ct.apply(p);
34098                         if(!c.locked){
34099                             cb[cb.length] = markup;
34100                         }else{
34101                             lcb[lcb.length] = markup;
34102                         }
34103                     }
34104                     var alt = [];
34105                     if(stripe && ((rowIndex+1) % 2 == 0)){
34106                         alt.push( "x-grid-row-alt");
34107                     }
34108                     if(r.dirty){
34109                         alt.push(" x-grid-dirty-row");
34110                     }
34111                     rp.cells = lcb;
34112                     if(this.getRowClass){
34113                         alt.push( this.getRowClass(r, rowIndex));
34114                     }
34115                     if (hasListener) {
34116                         rowcfg = {
34117                              
34118                             record: r,
34119                             rowIndex : rowIndex,
34120                             rowClass : ''
34121                         };
34122                         this.grid.fireEvent('rowclass', this, rowcfg);
34123                         alt.push(rowcfg.rowClass);
34124                     }
34125                     
34126                     rp.alt = alt.join(" ");
34127                     rp.cells = lcb.join("");
34128                     lbuf[lbuf.length] = rt.apply(rp);
34129                     rp.cells = cb.join("");
34130                     buf[buf.length] =  rt.apply(rp);
34131                 }
34132                 return [lbuf.join(""), buf.join("")];
34133             },
34134
34135     renderBody : function(){
34136         var markup = this.renderRows();
34137         var bt = this.templates.body;
34138         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
34139     },
34140
34141     /**
34142      * Refreshes the grid
34143      * @param {Boolean} headersToo
34144      */
34145     refresh : function(headersToo){
34146         this.fireEvent("beforerefresh", this);
34147         this.grid.stopEditing();
34148         var result = this.renderBody();
34149         this.lockedBody.update(result[0]);
34150         this.mainBody.update(result[1]);
34151         if(headersToo === true){
34152             this.updateHeaders();
34153             this.updateColumns();
34154             this.updateSplitters();
34155             this.updateHeaderSortState();
34156         }
34157         this.syncRowHeights();
34158         this.layout();
34159         this.fireEvent("refresh", this);
34160     },
34161
34162     handleColumnMove : function(cm, oldIndex, newIndex){
34163         this.indexMap = null;
34164         var s = this.getScrollState();
34165         this.refresh(true);
34166         this.restoreScroll(s);
34167         this.afterMove(newIndex);
34168     },
34169
34170     afterMove : function(colIndex){
34171         if(this.enableMoveAnim && Roo.enableFx){
34172             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
34173         }
34174         // if multisort - fix sortOrder, and reload..
34175         if (this.grid.dataSource.multiSort) {
34176             // the we can call sort again..
34177             var dm = this.grid.dataSource;
34178             var cm = this.grid.colModel;
34179             var so = [];
34180             for(var i = 0; i < cm.config.length; i++ ) {
34181                 
34182                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
34183                     continue; // dont' bother, it's not in sort list or being set.
34184                 }
34185                 
34186                 so.push(cm.config[i].dataIndex);
34187             };
34188             dm.sortOrder = so;
34189             dm.load(dm.lastOptions);
34190             
34191             
34192         }
34193         
34194     },
34195
34196     updateCell : function(dm, rowIndex, dataIndex){
34197         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
34198         if(typeof colIndex == "undefined"){ // not present in grid
34199             return;
34200         }
34201         var cm = this.grid.colModel;
34202         var cell = this.getCell(rowIndex, colIndex);
34203         var cellText = this.getCellText(rowIndex, colIndex);
34204
34205         var p = {
34206             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
34207             id : cm.getColumnId(colIndex),
34208             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
34209         };
34210         var renderer = cm.getRenderer(colIndex);
34211         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
34212         if(typeof val == "undefined" || val === "") {
34213             val = "&#160;";
34214         }
34215         cellText.innerHTML = val;
34216         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
34217         this.syncRowHeights(rowIndex, rowIndex);
34218     },
34219
34220     calcColumnWidth : function(colIndex, maxRowsToMeasure){
34221         var maxWidth = 0;
34222         if(this.grid.autoSizeHeaders){
34223             var h = this.getHeaderCellMeasure(colIndex);
34224             maxWidth = Math.max(maxWidth, h.scrollWidth);
34225         }
34226         var tb, index;
34227         if(this.cm.isLocked(colIndex)){
34228             tb = this.getLockedTable();
34229             index = colIndex;
34230         }else{
34231             tb = this.getBodyTable();
34232             index = colIndex - this.cm.getLockedCount();
34233         }
34234         if(tb && tb.rows){
34235             var rows = tb.rows;
34236             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
34237             for(var i = 0; i < stopIndex; i++){
34238                 var cell = rows[i].childNodes[index].firstChild;
34239                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
34240             }
34241         }
34242         return maxWidth + /*margin for error in IE*/ 5;
34243     },
34244     /**
34245      * Autofit a column to its content.
34246      * @param {Number} colIndex
34247      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
34248      */
34249      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
34250          if(this.cm.isHidden(colIndex)){
34251              return; // can't calc a hidden column
34252          }
34253         if(forceMinSize){
34254             var cid = this.cm.getColumnId(colIndex);
34255             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
34256            if(this.grid.autoSizeHeaders){
34257                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
34258            }
34259         }
34260         var newWidth = this.calcColumnWidth(colIndex);
34261         this.cm.setColumnWidth(colIndex,
34262             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
34263         if(!suppressEvent){
34264             this.grid.fireEvent("columnresize", colIndex, newWidth);
34265         }
34266     },
34267
34268     /**
34269      * Autofits all columns to their content and then expands to fit any extra space in the grid
34270      */
34271      autoSizeColumns : function(){
34272         var cm = this.grid.colModel;
34273         var colCount = cm.getColumnCount();
34274         for(var i = 0; i < colCount; i++){
34275             this.autoSizeColumn(i, true, true);
34276         }
34277         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
34278             this.fitColumns();
34279         }else{
34280             this.updateColumns();
34281             this.layout();
34282         }
34283     },
34284
34285     /**
34286      * Autofits all columns to the grid's width proportionate with their current size
34287      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
34288      */
34289     fitColumns : function(reserveScrollSpace){
34290         var cm = this.grid.colModel;
34291         var colCount = cm.getColumnCount();
34292         var cols = [];
34293         var width = 0;
34294         var i, w;
34295         for (i = 0; i < colCount; i++){
34296             if(!cm.isHidden(i) && !cm.isFixed(i)){
34297                 w = cm.getColumnWidth(i);
34298                 cols.push(i);
34299                 cols.push(w);
34300                 width += w;
34301             }
34302         }
34303         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
34304         if(reserveScrollSpace){
34305             avail -= 17;
34306         }
34307         var frac = (avail - cm.getTotalWidth())/width;
34308         while (cols.length){
34309             w = cols.pop();
34310             i = cols.pop();
34311             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
34312         }
34313         this.updateColumns();
34314         this.layout();
34315     },
34316
34317     onRowSelect : function(rowIndex){
34318         var row = this.getRowComposite(rowIndex);
34319         row.addClass("x-grid-row-selected");
34320     },
34321
34322     onRowDeselect : function(rowIndex){
34323         var row = this.getRowComposite(rowIndex);
34324         row.removeClass("x-grid-row-selected");
34325     },
34326
34327     onCellSelect : function(row, col){
34328         var cell = this.getCell(row, col);
34329         if(cell){
34330             Roo.fly(cell).addClass("x-grid-cell-selected");
34331         }
34332     },
34333
34334     onCellDeselect : function(row, col){
34335         var cell = this.getCell(row, col);
34336         if(cell){
34337             Roo.fly(cell).removeClass("x-grid-cell-selected");
34338         }
34339     },
34340
34341     updateHeaderSortState : function(){
34342         
34343         // sort state can be single { field: xxx, direction : yyy}
34344         // or   { xxx=>ASC , yyy : DESC ..... }
34345         
34346         var mstate = {};
34347         if (!this.ds.multiSort) { 
34348             var state = this.ds.getSortState();
34349             if(!state){
34350                 return;
34351             }
34352             mstate[state.field] = state.direction;
34353             // FIXME... - this is not used here.. but might be elsewhere..
34354             this.sortState = state;
34355             
34356         } else {
34357             mstate = this.ds.sortToggle;
34358         }
34359         //remove existing sort classes..
34360         
34361         var sc = this.sortClasses;
34362         var hds = this.el.select(this.headerSelector).removeClass(sc);
34363         
34364         for(var f in mstate) {
34365         
34366             var sortColumn = this.cm.findColumnIndex(f);
34367             
34368             if(sortColumn != -1){
34369                 var sortDir = mstate[f];        
34370                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
34371             }
34372         }
34373         
34374          
34375         
34376     },
34377
34378
34379     handleHeaderClick : function(g, index,e){
34380         
34381         Roo.log("header click");
34382         
34383         if (Roo.isTouch) {
34384             // touch events on header are handled by context
34385             this.handleHdCtx(g,index,e);
34386             return;
34387         }
34388         
34389         
34390         if(this.headersDisabled){
34391             return;
34392         }
34393         var dm = g.dataSource, cm = g.colModel;
34394         if(!cm.isSortable(index)){
34395             return;
34396         }
34397         g.stopEditing();
34398         
34399         if (dm.multiSort) {
34400             // update the sortOrder
34401             var so = [];
34402             for(var i = 0; i < cm.config.length; i++ ) {
34403                 
34404                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
34405                     continue; // dont' bother, it's not in sort list or being set.
34406                 }
34407                 
34408                 so.push(cm.config[i].dataIndex);
34409             };
34410             dm.sortOrder = so;
34411         }
34412         
34413         
34414         dm.sort(cm.getDataIndex(index));
34415     },
34416
34417
34418     destroy : function(){
34419         if(this.colMenu){
34420             this.colMenu.removeAll();
34421             Roo.menu.MenuMgr.unregister(this.colMenu);
34422             this.colMenu.getEl().remove();
34423             delete this.colMenu;
34424         }
34425         if(this.hmenu){
34426             this.hmenu.removeAll();
34427             Roo.menu.MenuMgr.unregister(this.hmenu);
34428             this.hmenu.getEl().remove();
34429             delete this.hmenu;
34430         }
34431         if(this.grid.enableColumnMove){
34432             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34433             if(dds){
34434                 for(var dd in dds){
34435                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
34436                         var elid = dds[dd].dragElId;
34437                         dds[dd].unreg();
34438                         Roo.get(elid).remove();
34439                     } else if(dds[dd].config.isTarget){
34440                         dds[dd].proxyTop.remove();
34441                         dds[dd].proxyBottom.remove();
34442                         dds[dd].unreg();
34443                     }
34444                     if(Roo.dd.DDM.locationCache[dd]){
34445                         delete Roo.dd.DDM.locationCache[dd];
34446                     }
34447                 }
34448                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
34449             }
34450         }
34451         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
34452         this.bind(null, null);
34453         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
34454     },
34455
34456     handleLockChange : function(){
34457         this.refresh(true);
34458     },
34459
34460     onDenyColumnLock : function(){
34461
34462     },
34463
34464     onDenyColumnHide : function(){
34465
34466     },
34467
34468     handleHdMenuClick : function(item){
34469         var index = this.hdCtxIndex;
34470         var cm = this.cm, ds = this.ds;
34471         switch(item.id){
34472             case "asc":
34473                 ds.sort(cm.getDataIndex(index), "ASC");
34474                 break;
34475             case "desc":
34476                 ds.sort(cm.getDataIndex(index), "DESC");
34477                 break;
34478             case "lock":
34479                 var lc = cm.getLockedCount();
34480                 if(cm.getColumnCount(true) <= lc+1){
34481                     this.onDenyColumnLock();
34482                     return;
34483                 }
34484                 if(lc != index){
34485                     cm.setLocked(index, true, true);
34486                     cm.moveColumn(index, lc);
34487                     this.grid.fireEvent("columnmove", index, lc);
34488                 }else{
34489                     cm.setLocked(index, true);
34490                 }
34491             break;
34492             case "unlock":
34493                 var lc = cm.getLockedCount();
34494                 if((lc-1) != index){
34495                     cm.setLocked(index, false, true);
34496                     cm.moveColumn(index, lc-1);
34497                     this.grid.fireEvent("columnmove", index, lc-1);
34498                 }else{
34499                     cm.setLocked(index, false);
34500                 }
34501             break;
34502             case 'wider': // used to expand cols on touch..
34503             case 'narrow':
34504                 var cw = cm.getColumnWidth(index);
34505                 cw += (item.id == 'wider' ? 1 : -1) * 50;
34506                 cw = Math.max(0, cw);
34507                 cw = Math.min(cw,4000);
34508                 cm.setColumnWidth(index, cw);
34509                 break;
34510                 
34511             default:
34512                 index = cm.getIndexById(item.id.substr(4));
34513                 if(index != -1){
34514                     if(item.checked && cm.getColumnCount(true) <= 1){
34515                         this.onDenyColumnHide();
34516                         return false;
34517                     }
34518                     cm.setHidden(index, item.checked);
34519                 }
34520         }
34521         return true;
34522     },
34523
34524     beforeColMenuShow : function(){
34525         var cm = this.cm,  colCount = cm.getColumnCount();
34526         this.colMenu.removeAll();
34527         for(var i = 0; i < colCount; i++){
34528             this.colMenu.add(new Roo.menu.CheckItem({
34529                 id: "col-"+cm.getColumnId(i),
34530                 text: cm.getColumnHeader(i),
34531                 checked: !cm.isHidden(i),
34532                 hideOnClick:false
34533             }));
34534         }
34535     },
34536
34537     handleHdCtx : function(g, index, e){
34538         e.stopEvent();
34539         var hd = this.getHeaderCell(index);
34540         this.hdCtxIndex = index;
34541         var ms = this.hmenu.items, cm = this.cm;
34542         ms.get("asc").setDisabled(!cm.isSortable(index));
34543         ms.get("desc").setDisabled(!cm.isSortable(index));
34544         if(this.grid.enableColLock !== false){
34545             ms.get("lock").setDisabled(cm.isLocked(index));
34546             ms.get("unlock").setDisabled(!cm.isLocked(index));
34547         }
34548         this.hmenu.show(hd, "tl-bl");
34549     },
34550
34551     handleHdOver : function(e){
34552         var hd = this.findHeaderCell(e.getTarget());
34553         if(hd && !this.headersDisabled){
34554             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
34555                this.fly(hd).addClass("x-grid-hd-over");
34556             }
34557         }
34558     },
34559
34560     handleHdOut : function(e){
34561         var hd = this.findHeaderCell(e.getTarget());
34562         if(hd){
34563             this.fly(hd).removeClass("x-grid-hd-over");
34564         }
34565     },
34566
34567     handleSplitDblClick : function(e, t){
34568         var i = this.getCellIndex(t);
34569         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
34570             this.autoSizeColumn(i, true);
34571             this.layout();
34572         }
34573     },
34574
34575     render : function(){
34576
34577         var cm = this.cm;
34578         var colCount = cm.getColumnCount();
34579
34580         if(this.grid.monitorWindowResize === true){
34581             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34582         }
34583         var header = this.renderHeaders();
34584         var body = this.templates.body.apply({rows:""});
34585         var html = this.templates.master.apply({
34586             lockedBody: body,
34587             body: body,
34588             lockedHeader: header[0],
34589             header: header[1]
34590         });
34591
34592         //this.updateColumns();
34593
34594         this.grid.getGridEl().dom.innerHTML = html;
34595
34596         this.initElements();
34597         
34598         // a kludge to fix the random scolling effect in webkit
34599         this.el.on("scroll", function() {
34600             this.el.dom.scrollTop=0; // hopefully not recursive..
34601         },this);
34602
34603         this.scroller.on("scroll", this.handleScroll, this);
34604         this.lockedBody.on("mousewheel", this.handleWheel, this);
34605         this.mainBody.on("mousewheel", this.handleWheel, this);
34606
34607         this.mainHd.on("mouseover", this.handleHdOver, this);
34608         this.mainHd.on("mouseout", this.handleHdOut, this);
34609         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
34610                 {delegate: "."+this.splitClass});
34611
34612         this.lockedHd.on("mouseover", this.handleHdOver, this);
34613         this.lockedHd.on("mouseout", this.handleHdOut, this);
34614         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
34615                 {delegate: "."+this.splitClass});
34616
34617         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
34618             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34619         }
34620
34621         this.updateSplitters();
34622
34623         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
34624             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34625             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
34626         }
34627
34628         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
34629             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
34630             this.hmenu.add(
34631                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
34632                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
34633             );
34634             if(this.grid.enableColLock !== false){
34635                 this.hmenu.add('-',
34636                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
34637                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
34638                 );
34639             }
34640             if (Roo.isTouch) {
34641                  this.hmenu.add('-',
34642                     {id:"wider", text: this.columnsWiderText},
34643                     {id:"narrow", text: this.columnsNarrowText }
34644                 );
34645                 
34646                  
34647             }
34648             
34649             if(this.grid.enableColumnHide !== false){
34650
34651                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
34652                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
34653                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
34654
34655                 this.hmenu.add('-',
34656                     {id:"columns", text: this.columnsText, menu: this.colMenu}
34657                 );
34658             }
34659             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
34660
34661             this.grid.on("headercontextmenu", this.handleHdCtx, this);
34662         }
34663
34664         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
34665             this.dd = new Roo.grid.GridDragZone(this.grid, {
34666                 ddGroup : this.grid.ddGroup || 'GridDD'
34667             });
34668             
34669         }
34670
34671         /*
34672         for(var i = 0; i < colCount; i++){
34673             if(cm.isHidden(i)){
34674                 this.hideColumn(i);
34675             }
34676             if(cm.config[i].align){
34677                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
34678                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
34679             }
34680         }*/
34681         
34682         this.updateHeaderSortState();
34683
34684         this.beforeInitialResize();
34685         this.layout(true);
34686
34687         // two part rendering gives faster view to the user
34688         this.renderPhase2.defer(1, this);
34689     },
34690
34691     renderPhase2 : function(){
34692         // render the rows now
34693         this.refresh();
34694         if(this.grid.autoSizeColumns){
34695             this.autoSizeColumns();
34696         }
34697     },
34698
34699     beforeInitialResize : function(){
34700
34701     },
34702
34703     onColumnSplitterMoved : function(i, w){
34704         this.userResized = true;
34705         var cm = this.grid.colModel;
34706         cm.setColumnWidth(i, w, true);
34707         var cid = cm.getColumnId(i);
34708         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34709         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
34710         this.updateSplitters();
34711         this.layout();
34712         this.grid.fireEvent("columnresize", i, w);
34713     },
34714
34715     syncRowHeights : function(startIndex, endIndex){
34716         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
34717             startIndex = startIndex || 0;
34718             var mrows = this.getBodyTable().rows;
34719             var lrows = this.getLockedTable().rows;
34720             var len = mrows.length-1;
34721             endIndex = Math.min(endIndex || len, len);
34722             for(var i = startIndex; i <= endIndex; i++){
34723                 var m = mrows[i], l = lrows[i];
34724                 var h = Math.max(m.offsetHeight, l.offsetHeight);
34725                 m.style.height = l.style.height = h + "px";
34726             }
34727         }
34728     },
34729
34730     layout : function(initialRender, is2ndPass){
34731         var g = this.grid;
34732         var auto = g.autoHeight;
34733         var scrollOffset = 16;
34734         var c = g.getGridEl(), cm = this.cm,
34735                 expandCol = g.autoExpandColumn,
34736                 gv = this;
34737         //c.beginMeasure();
34738
34739         if(!c.dom.offsetWidth){ // display:none?
34740             if(initialRender){
34741                 this.lockedWrap.show();
34742                 this.mainWrap.show();
34743             }
34744             return;
34745         }
34746
34747         var hasLock = this.cm.isLocked(0);
34748
34749         var tbh = this.headerPanel.getHeight();
34750         var bbh = this.footerPanel.getHeight();
34751
34752         if(auto){
34753             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
34754             var newHeight = ch + c.getBorderWidth("tb");
34755             if(g.maxHeight){
34756                 newHeight = Math.min(g.maxHeight, newHeight);
34757             }
34758             c.setHeight(newHeight);
34759         }
34760
34761         if(g.autoWidth){
34762             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
34763         }
34764
34765         var s = this.scroller;
34766
34767         var csize = c.getSize(true);
34768
34769         this.el.setSize(csize.width, csize.height);
34770
34771         this.headerPanel.setWidth(csize.width);
34772         this.footerPanel.setWidth(csize.width);
34773
34774         var hdHeight = this.mainHd.getHeight();
34775         var vw = csize.width;
34776         var vh = csize.height - (tbh + bbh);
34777
34778         s.setSize(vw, vh);
34779
34780         var bt = this.getBodyTable();
34781         
34782         if(cm.getLockedCount() == cm.config.length){
34783             bt = this.getLockedTable();
34784         }
34785         
34786         var ltWidth = hasLock ?
34787                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
34788
34789         var scrollHeight = bt.offsetHeight;
34790         var scrollWidth = ltWidth + bt.offsetWidth;
34791         var vscroll = false, hscroll = false;
34792
34793         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
34794
34795         var lw = this.lockedWrap, mw = this.mainWrap;
34796         var lb = this.lockedBody, mb = this.mainBody;
34797
34798         setTimeout(function(){
34799             var t = s.dom.offsetTop;
34800             var w = s.dom.clientWidth,
34801                 h = s.dom.clientHeight;
34802
34803             lw.setTop(t);
34804             lw.setSize(ltWidth, h);
34805
34806             mw.setLeftTop(ltWidth, t);
34807             mw.setSize(w-ltWidth, h);
34808
34809             lb.setHeight(h-hdHeight);
34810             mb.setHeight(h-hdHeight);
34811
34812             if(is2ndPass !== true && !gv.userResized && expandCol){
34813                 // high speed resize without full column calculation
34814                 
34815                 var ci = cm.getIndexById(expandCol);
34816                 if (ci < 0) {
34817                     ci = cm.findColumnIndex(expandCol);
34818                 }
34819                 ci = Math.max(0, ci); // make sure it's got at least the first col.
34820                 var expandId = cm.getColumnId(ci);
34821                 var  tw = cm.getTotalWidth(false);
34822                 var currentWidth = cm.getColumnWidth(ci);
34823                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
34824                 if(currentWidth != cw){
34825                     cm.setColumnWidth(ci, cw, true);
34826                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34827                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
34828                     gv.updateSplitters();
34829                     gv.layout(false, true);
34830                 }
34831             }
34832
34833             if(initialRender){
34834                 lw.show();
34835                 mw.show();
34836             }
34837             //c.endMeasure();
34838         }, 10);
34839     },
34840
34841     onWindowResize : function(){
34842         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
34843             return;
34844         }
34845         this.layout();
34846     },
34847
34848     appendFooter : function(parentEl){
34849         return null;
34850     },
34851
34852     sortAscText : "Sort Ascending",
34853     sortDescText : "Sort Descending",
34854     lockText : "Lock Column",
34855     unlockText : "Unlock Column",
34856     columnsText : "Columns",
34857  
34858     columnsWiderText : "Wider",
34859     columnsNarrowText : "Thinner"
34860 });
34861
34862
34863 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
34864     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
34865     this.proxy.el.addClass('x-grid3-col-dd');
34866 };
34867
34868 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
34869     handleMouseDown : function(e){
34870
34871     },
34872
34873     callHandleMouseDown : function(e){
34874         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
34875     }
34876 });
34877 /*
34878  * Based on:
34879  * Ext JS Library 1.1.1
34880  * Copyright(c) 2006-2007, Ext JS, LLC.
34881  *
34882  * Originally Released Under LGPL - original licence link has changed is not relivant.
34883  *
34884  * Fork - LGPL
34885  * <script type="text/javascript">
34886  */
34887  
34888 // private
34889 // This is a support class used internally by the Grid components
34890 Roo.grid.SplitDragZone = function(grid, hd, hd2){
34891     this.grid = grid;
34892     this.view = grid.getView();
34893     this.proxy = this.view.resizeProxy;
34894     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
34895         "gridSplitters" + this.grid.getGridEl().id, {
34896         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
34897     });
34898     this.setHandleElId(Roo.id(hd));
34899     this.setOuterHandleElId(Roo.id(hd2));
34900     this.scroll = false;
34901 };
34902 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
34903     fly: Roo.Element.fly,
34904
34905     b4StartDrag : function(x, y){
34906         this.view.headersDisabled = true;
34907         this.proxy.setHeight(this.view.mainWrap.getHeight());
34908         var w = this.cm.getColumnWidth(this.cellIndex);
34909         var minw = Math.max(w-this.grid.minColumnWidth, 0);
34910         this.resetConstraints();
34911         this.setXConstraint(minw, 1000);
34912         this.setYConstraint(0, 0);
34913         this.minX = x - minw;
34914         this.maxX = x + 1000;
34915         this.startPos = x;
34916         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
34917     },
34918
34919
34920     handleMouseDown : function(e){
34921         ev = Roo.EventObject.setEvent(e);
34922         var t = this.fly(ev.getTarget());
34923         if(t.hasClass("x-grid-split")){
34924             this.cellIndex = this.view.getCellIndex(t.dom);
34925             this.split = t.dom;
34926             this.cm = this.grid.colModel;
34927             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
34928                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
34929             }
34930         }
34931     },
34932
34933     endDrag : function(e){
34934         this.view.headersDisabled = false;
34935         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
34936         var diff = endX - this.startPos;
34937         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
34938     },
34939
34940     autoOffset : function(){
34941         this.setDelta(0,0);
34942     }
34943 });/*
34944  * Based on:
34945  * Ext JS Library 1.1.1
34946  * Copyright(c) 2006-2007, Ext JS, LLC.
34947  *
34948  * Originally Released Under LGPL - original licence link has changed is not relivant.
34949  *
34950  * Fork - LGPL
34951  * <script type="text/javascript">
34952  */
34953  
34954 // private
34955 // This is a support class used internally by the Grid components
34956 Roo.grid.GridDragZone = function(grid, config){
34957     this.view = grid.getView();
34958     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
34959     if(this.view.lockedBody){
34960         this.setHandleElId(Roo.id(this.view.mainBody.dom));
34961         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
34962     }
34963     this.scroll = false;
34964     this.grid = grid;
34965     this.ddel = document.createElement('div');
34966     this.ddel.className = 'x-grid-dd-wrap';
34967 };
34968
34969 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
34970     ddGroup : "GridDD",
34971
34972     getDragData : function(e){
34973         var t = Roo.lib.Event.getTarget(e);
34974         var rowIndex = this.view.findRowIndex(t);
34975         var sm = this.grid.selModel;
34976             
34977         //Roo.log(rowIndex);
34978         
34979         if (sm.getSelectedCell) {
34980             // cell selection..
34981             if (!sm.getSelectedCell()) {
34982                 return false;
34983             }
34984             if (rowIndex != sm.getSelectedCell()[0]) {
34985                 return false;
34986             }
34987         
34988         }
34989         
34990         if(rowIndex !== false){
34991             
34992             // if editorgrid.. 
34993             
34994             
34995             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
34996                
34997             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
34998               //  
34999             //}
35000             if (e.hasModifier()){
35001                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
35002             }
35003             
35004             Roo.log("getDragData");
35005             
35006             return {
35007                 grid: this.grid,
35008                 ddel: this.ddel,
35009                 rowIndex: rowIndex,
35010                 selections:sm.getSelections ? sm.getSelections() : (
35011                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
35012                 )
35013             };
35014         }
35015         return false;
35016     },
35017
35018     onInitDrag : function(e){
35019         var data = this.dragData;
35020         this.ddel.innerHTML = this.grid.getDragDropText();
35021         this.proxy.update(this.ddel);
35022         // fire start drag?
35023     },
35024
35025     afterRepair : function(){
35026         this.dragging = false;
35027     },
35028
35029     getRepairXY : function(e, data){
35030         return false;
35031     },
35032
35033     onEndDrag : function(data, e){
35034         // fire end drag?
35035     },
35036
35037     onValidDrop : function(dd, e, id){
35038         // fire drag drop?
35039         this.hideProxy();
35040     },
35041
35042     beforeInvalidDrop : function(e, id){
35043
35044     }
35045 });/*
35046  * Based on:
35047  * Ext JS Library 1.1.1
35048  * Copyright(c) 2006-2007, Ext JS, LLC.
35049  *
35050  * Originally Released Under LGPL - original licence link has changed is not relivant.
35051  *
35052  * Fork - LGPL
35053  * <script type="text/javascript">
35054  */
35055  
35056
35057 /**
35058  * @class Roo.grid.ColumnModel
35059  * @extends Roo.util.Observable
35060  * This is the default implementation of a ColumnModel used by the Grid. It defines
35061  * the columns in the grid.
35062  * <br>Usage:<br>
35063  <pre><code>
35064  var colModel = new Roo.grid.ColumnModel([
35065         {header: "Ticker", width: 60, sortable: true, locked: true},
35066         {header: "Company Name", width: 150, sortable: true},
35067         {header: "Market Cap.", width: 100, sortable: true},
35068         {header: "$ Sales", width: 100, sortable: true, renderer: money},
35069         {header: "Employees", width: 100, sortable: true, resizable: false}
35070  ]);
35071  </code></pre>
35072  * <p>
35073  
35074  * The config options listed for this class are options which may appear in each
35075  * individual column definition.
35076  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
35077  * @constructor
35078  * @param {Object} config An Array of column config objects. See this class's
35079  * config objects for details.
35080 */
35081 Roo.grid.ColumnModel = function(config){
35082         /**
35083      * The config passed into the constructor
35084      */
35085     this.config = config;
35086     this.lookup = {};
35087
35088     // if no id, create one
35089     // if the column does not have a dataIndex mapping,
35090     // map it to the order it is in the config
35091     for(var i = 0, len = config.length; i < len; i++){
35092         var c = config[i];
35093         if(typeof c.dataIndex == "undefined"){
35094             c.dataIndex = i;
35095         }
35096         if(typeof c.renderer == "string"){
35097             c.renderer = Roo.util.Format[c.renderer];
35098         }
35099         if(typeof c.id == "undefined"){
35100             c.id = Roo.id();
35101         }
35102         if(c.editor && c.editor.xtype){
35103             c.editor  = Roo.factory(c.editor, Roo.grid);
35104         }
35105         if(c.editor && c.editor.isFormField){
35106             c.editor = new Roo.grid.GridEditor(c.editor);
35107         }
35108         this.lookup[c.id] = c;
35109     }
35110
35111     /**
35112      * The width of columns which have no width specified (defaults to 100)
35113      * @type Number
35114      */
35115     this.defaultWidth = 100;
35116
35117     /**
35118      * Default sortable of columns which have no sortable specified (defaults to false)
35119      * @type Boolean
35120      */
35121     this.defaultSortable = false;
35122
35123     this.addEvents({
35124         /**
35125              * @event widthchange
35126              * Fires when the width of a column changes.
35127              * @param {ColumnModel} this
35128              * @param {Number} columnIndex The column index
35129              * @param {Number} newWidth The new width
35130              */
35131             "widthchange": true,
35132         /**
35133              * @event headerchange
35134              * Fires when the text of a header changes.
35135              * @param {ColumnModel} this
35136              * @param {Number} columnIndex The column index
35137              * @param {Number} newText The new header text
35138              */
35139             "headerchange": true,
35140         /**
35141              * @event hiddenchange
35142              * Fires when a column is hidden or "unhidden".
35143              * @param {ColumnModel} this
35144              * @param {Number} columnIndex The column index
35145              * @param {Boolean} hidden true if hidden, false otherwise
35146              */
35147             "hiddenchange": true,
35148             /**
35149          * @event columnmoved
35150          * Fires when a column is moved.
35151          * @param {ColumnModel} this
35152          * @param {Number} oldIndex
35153          * @param {Number} newIndex
35154          */
35155         "columnmoved" : true,
35156         /**
35157          * @event columlockchange
35158          * Fires when a column's locked state is changed
35159          * @param {ColumnModel} this
35160          * @param {Number} colIndex
35161          * @param {Boolean} locked true if locked
35162          */
35163         "columnlockchange" : true
35164     });
35165     Roo.grid.ColumnModel.superclass.constructor.call(this);
35166 };
35167 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
35168     /**
35169      * @cfg {String} header The header text to display in the Grid view.
35170      */
35171     /**
35172      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
35173      * {@link Roo.data.Record} definition from which to draw the column's value. If not
35174      * specified, the column's index is used as an index into the Record's data Array.
35175      */
35176     /**
35177      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
35178      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
35179      */
35180     /**
35181      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
35182      * Defaults to the value of the {@link #defaultSortable} property.
35183      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
35184      */
35185     /**
35186      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
35187      */
35188     /**
35189      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
35190      */
35191     /**
35192      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
35193      */
35194     /**
35195      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
35196      */
35197     /**
35198      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
35199      * given the cell's data value. See {@link #setRenderer}. If not specified, the
35200      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
35201      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
35202      */
35203        /**
35204      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
35205      */
35206     /**
35207      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
35208      */
35209     /**
35210      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
35211      */
35212     /**
35213      * @cfg {String} cursor (Optional)
35214      */
35215     /**
35216      * @cfg {String} tooltip (Optional)
35217      */
35218     /**
35219      * @cfg {Number} xs (Optional)
35220      */
35221     /**
35222      * @cfg {Number} sm (Optional)
35223      */
35224     /**
35225      * @cfg {Number} md (Optional)
35226      */
35227     /**
35228      * @cfg {Number} lg (Optional)
35229      */
35230     /**
35231      * Returns the id of the column at the specified index.
35232      * @param {Number} index The column index
35233      * @return {String} the id
35234      */
35235     getColumnId : function(index){
35236         return this.config[index].id;
35237     },
35238
35239     /**
35240      * Returns the column for a specified id.
35241      * @param {String} id The column id
35242      * @return {Object} the column
35243      */
35244     getColumnById : function(id){
35245         return this.lookup[id];
35246     },
35247
35248     
35249     /**
35250      * Returns the column for a specified dataIndex.
35251      * @param {String} dataIndex The column dataIndex
35252      * @return {Object|Boolean} the column or false if not found
35253      */
35254     getColumnByDataIndex: function(dataIndex){
35255         var index = this.findColumnIndex(dataIndex);
35256         return index > -1 ? this.config[index] : false;
35257     },
35258     
35259     /**
35260      * Returns the index for a specified column id.
35261      * @param {String} id The column id
35262      * @return {Number} the index, or -1 if not found
35263      */
35264     getIndexById : function(id){
35265         for(var i = 0, len = this.config.length; i < len; i++){
35266             if(this.config[i].id == id){
35267                 return i;
35268             }
35269         }
35270         return -1;
35271     },
35272     
35273     /**
35274      * Returns the index for a specified column dataIndex.
35275      * @param {String} dataIndex The column dataIndex
35276      * @return {Number} the index, or -1 if not found
35277      */
35278     
35279     findColumnIndex : function(dataIndex){
35280         for(var i = 0, len = this.config.length; i < len; i++){
35281             if(this.config[i].dataIndex == dataIndex){
35282                 return i;
35283             }
35284         }
35285         return -1;
35286     },
35287     
35288     
35289     moveColumn : function(oldIndex, newIndex){
35290         var c = this.config[oldIndex];
35291         this.config.splice(oldIndex, 1);
35292         this.config.splice(newIndex, 0, c);
35293         this.dataMap = null;
35294         this.fireEvent("columnmoved", this, oldIndex, newIndex);
35295     },
35296
35297     isLocked : function(colIndex){
35298         return this.config[colIndex].locked === true;
35299     },
35300
35301     setLocked : function(colIndex, value, suppressEvent){
35302         if(this.isLocked(colIndex) == value){
35303             return;
35304         }
35305         this.config[colIndex].locked = value;
35306         if(!suppressEvent){
35307             this.fireEvent("columnlockchange", this, colIndex, value);
35308         }
35309     },
35310
35311     getTotalLockedWidth : function(){
35312         var totalWidth = 0;
35313         for(var i = 0; i < this.config.length; i++){
35314             if(this.isLocked(i) && !this.isHidden(i)){
35315                 this.totalWidth += this.getColumnWidth(i);
35316             }
35317         }
35318         return totalWidth;
35319     },
35320
35321     getLockedCount : function(){
35322         for(var i = 0, len = this.config.length; i < len; i++){
35323             if(!this.isLocked(i)){
35324                 return i;
35325             }
35326         }
35327         
35328         return this.config.length;
35329     },
35330
35331     /**
35332      * Returns the number of columns.
35333      * @return {Number}
35334      */
35335     getColumnCount : function(visibleOnly){
35336         if(visibleOnly === true){
35337             var c = 0;
35338             for(var i = 0, len = this.config.length; i < len; i++){
35339                 if(!this.isHidden(i)){
35340                     c++;
35341                 }
35342             }
35343             return c;
35344         }
35345         return this.config.length;
35346     },
35347
35348     /**
35349      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
35350      * @param {Function} fn
35351      * @param {Object} scope (optional)
35352      * @return {Array} result
35353      */
35354     getColumnsBy : function(fn, scope){
35355         var r = [];
35356         for(var i = 0, len = this.config.length; i < len; i++){
35357             var c = this.config[i];
35358             if(fn.call(scope||this, c, i) === true){
35359                 r[r.length] = c;
35360             }
35361         }
35362         return r;
35363     },
35364
35365     /**
35366      * Returns true if the specified column is sortable.
35367      * @param {Number} col The column index
35368      * @return {Boolean}
35369      */
35370     isSortable : function(col){
35371         if(typeof this.config[col].sortable == "undefined"){
35372             return this.defaultSortable;
35373         }
35374         return this.config[col].sortable;
35375     },
35376
35377     /**
35378      * Returns the rendering (formatting) function defined for the column.
35379      * @param {Number} col The column index.
35380      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
35381      */
35382     getRenderer : function(col){
35383         if(!this.config[col].renderer){
35384             return Roo.grid.ColumnModel.defaultRenderer;
35385         }
35386         return this.config[col].renderer;
35387     },
35388
35389     /**
35390      * Sets the rendering (formatting) function for a column.
35391      * @param {Number} col The column index
35392      * @param {Function} fn The function to use to process the cell's raw data
35393      * to return HTML markup for the grid view. The render function is called with
35394      * the following parameters:<ul>
35395      * <li>Data value.</li>
35396      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
35397      * <li>css A CSS style string to apply to the table cell.</li>
35398      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
35399      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
35400      * <li>Row index</li>
35401      * <li>Column index</li>
35402      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
35403      */
35404     setRenderer : function(col, fn){
35405         this.config[col].renderer = fn;
35406     },
35407
35408     /**
35409      * Returns the width for the specified column.
35410      * @param {Number} col The column index
35411      * @return {Number}
35412      */
35413     getColumnWidth : function(col){
35414         return this.config[col].width * 1 || this.defaultWidth;
35415     },
35416
35417     /**
35418      * Sets the width for a column.
35419      * @param {Number} col The column index
35420      * @param {Number} width The new width
35421      */
35422     setColumnWidth : function(col, width, suppressEvent){
35423         this.config[col].width = width;
35424         this.totalWidth = null;
35425         if(!suppressEvent){
35426              this.fireEvent("widthchange", this, col, width);
35427         }
35428     },
35429
35430     /**
35431      * Returns the total width of all columns.
35432      * @param {Boolean} includeHidden True to include hidden column widths
35433      * @return {Number}
35434      */
35435     getTotalWidth : function(includeHidden){
35436         if(!this.totalWidth){
35437             this.totalWidth = 0;
35438             for(var i = 0, len = this.config.length; i < len; i++){
35439                 if(includeHidden || !this.isHidden(i)){
35440                     this.totalWidth += this.getColumnWidth(i);
35441                 }
35442             }
35443         }
35444         return this.totalWidth;
35445     },
35446
35447     /**
35448      * Returns the header for the specified column.
35449      * @param {Number} col The column index
35450      * @return {String}
35451      */
35452     getColumnHeader : function(col){
35453         return this.config[col].header;
35454     },
35455
35456     /**
35457      * Sets the header for a column.
35458      * @param {Number} col The column index
35459      * @param {String} header The new header
35460      */
35461     setColumnHeader : function(col, header){
35462         this.config[col].header = header;
35463         this.fireEvent("headerchange", this, col, header);
35464     },
35465
35466     /**
35467      * Returns the tooltip for the specified column.
35468      * @param {Number} col The column index
35469      * @return {String}
35470      */
35471     getColumnTooltip : function(col){
35472             return this.config[col].tooltip;
35473     },
35474     /**
35475      * Sets the tooltip for a column.
35476      * @param {Number} col The column index
35477      * @param {String} tooltip The new tooltip
35478      */
35479     setColumnTooltip : function(col, tooltip){
35480             this.config[col].tooltip = tooltip;
35481     },
35482
35483     /**
35484      * Returns the dataIndex for the specified column.
35485      * @param {Number} col The column index
35486      * @return {Number}
35487      */
35488     getDataIndex : function(col){
35489         return this.config[col].dataIndex;
35490     },
35491
35492     /**
35493      * Sets the dataIndex for a column.
35494      * @param {Number} col The column index
35495      * @param {Number} dataIndex The new dataIndex
35496      */
35497     setDataIndex : function(col, dataIndex){
35498         this.config[col].dataIndex = dataIndex;
35499     },
35500
35501     
35502     
35503     /**
35504      * Returns true if the cell is editable.
35505      * @param {Number} colIndex The column index
35506      * @param {Number} rowIndex The row index - this is nto actually used..?
35507      * @return {Boolean}
35508      */
35509     isCellEditable : function(colIndex, rowIndex){
35510         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
35511     },
35512
35513     /**
35514      * Returns the editor defined for the cell/column.
35515      * return false or null to disable editing.
35516      * @param {Number} colIndex The column index
35517      * @param {Number} rowIndex The row index
35518      * @return {Object}
35519      */
35520     getCellEditor : function(colIndex, rowIndex){
35521         return this.config[colIndex].editor;
35522     },
35523
35524     /**
35525      * Sets if a column is editable.
35526      * @param {Number} col The column index
35527      * @param {Boolean} editable True if the column is editable
35528      */
35529     setEditable : function(col, editable){
35530         this.config[col].editable = editable;
35531     },
35532
35533
35534     /**
35535      * Returns true if the column is hidden.
35536      * @param {Number} colIndex The column index
35537      * @return {Boolean}
35538      */
35539     isHidden : function(colIndex){
35540         return this.config[colIndex].hidden;
35541     },
35542
35543
35544     /**
35545      * Returns true if the column width cannot be changed
35546      */
35547     isFixed : function(colIndex){
35548         return this.config[colIndex].fixed;
35549     },
35550
35551     /**
35552      * Returns true if the column can be resized
35553      * @return {Boolean}
35554      */
35555     isResizable : function(colIndex){
35556         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
35557     },
35558     /**
35559      * Sets if a column is hidden.
35560      * @param {Number} colIndex The column index
35561      * @param {Boolean} hidden True if the column is hidden
35562      */
35563     setHidden : function(colIndex, hidden){
35564         this.config[colIndex].hidden = hidden;
35565         this.totalWidth = null;
35566         this.fireEvent("hiddenchange", this, colIndex, hidden);
35567     },
35568
35569     /**
35570      * Sets the editor for a column.
35571      * @param {Number} col The column index
35572      * @param {Object} editor The editor object
35573      */
35574     setEditor : function(col, editor){
35575         this.config[col].editor = editor;
35576     }
35577 });
35578
35579 Roo.grid.ColumnModel.defaultRenderer = function(value)
35580 {
35581     if(typeof value == "object") {
35582         return value;
35583     }
35584         if(typeof value == "string" && value.length < 1){
35585             return "&#160;";
35586         }
35587     
35588         return String.format("{0}", value);
35589 };
35590
35591 // Alias for backwards compatibility
35592 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
35593 /*
35594  * Based on:
35595  * Ext JS Library 1.1.1
35596  * Copyright(c) 2006-2007, Ext JS, LLC.
35597  *
35598  * Originally Released Under LGPL - original licence link has changed is not relivant.
35599  *
35600  * Fork - LGPL
35601  * <script type="text/javascript">
35602  */
35603
35604 /**
35605  * @class Roo.grid.AbstractSelectionModel
35606  * @extends Roo.util.Observable
35607  * Abstract base class for grid SelectionModels.  It provides the interface that should be
35608  * implemented by descendant classes.  This class should not be directly instantiated.
35609  * @constructor
35610  */
35611 Roo.grid.AbstractSelectionModel = function(){
35612     this.locked = false;
35613     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
35614 };
35615
35616 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
35617     /** @ignore Called by the grid automatically. Do not call directly. */
35618     init : function(grid){
35619         this.grid = grid;
35620         this.initEvents();
35621     },
35622
35623     /**
35624      * Locks the selections.
35625      */
35626     lock : function(){
35627         this.locked = true;
35628     },
35629
35630     /**
35631      * Unlocks the selections.
35632      */
35633     unlock : function(){
35634         this.locked = false;
35635     },
35636
35637     /**
35638      * Returns true if the selections are locked.
35639      * @return {Boolean}
35640      */
35641     isLocked : function(){
35642         return this.locked;
35643     }
35644 });/*
35645  * Based on:
35646  * Ext JS Library 1.1.1
35647  * Copyright(c) 2006-2007, Ext JS, LLC.
35648  *
35649  * Originally Released Under LGPL - original licence link has changed is not relivant.
35650  *
35651  * Fork - LGPL
35652  * <script type="text/javascript">
35653  */
35654 /**
35655  * @extends Roo.grid.AbstractSelectionModel
35656  * @class Roo.grid.RowSelectionModel
35657  * The default SelectionModel used by {@link Roo.grid.Grid}.
35658  * It supports multiple selections and keyboard selection/navigation. 
35659  * @constructor
35660  * @param {Object} config
35661  */
35662 Roo.grid.RowSelectionModel = function(config){
35663     Roo.apply(this, config);
35664     this.selections = new Roo.util.MixedCollection(false, function(o){
35665         return o.id;
35666     });
35667
35668     this.last = false;
35669     this.lastActive = false;
35670
35671     this.addEvents({
35672         /**
35673              * @event selectionchange
35674              * Fires when the selection changes
35675              * @param {SelectionModel} this
35676              */
35677             "selectionchange" : true,
35678         /**
35679              * @event afterselectionchange
35680              * Fires after the selection changes (eg. by key press or clicking)
35681              * @param {SelectionModel} this
35682              */
35683             "afterselectionchange" : true,
35684         /**
35685              * @event beforerowselect
35686              * Fires when a row is selected being selected, return false to cancel.
35687              * @param {SelectionModel} this
35688              * @param {Number} rowIndex The selected index
35689              * @param {Boolean} keepExisting False if other selections will be cleared
35690              */
35691             "beforerowselect" : true,
35692         /**
35693              * @event rowselect
35694              * Fires when a row is selected.
35695              * @param {SelectionModel} this
35696              * @param {Number} rowIndex The selected index
35697              * @param {Roo.data.Record} r The record
35698              */
35699             "rowselect" : true,
35700         /**
35701              * @event rowdeselect
35702              * Fires when a row is deselected.
35703              * @param {SelectionModel} this
35704              * @param {Number} rowIndex The selected index
35705              */
35706         "rowdeselect" : true
35707     });
35708     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
35709     this.locked = false;
35710 };
35711
35712 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
35713     /**
35714      * @cfg {Boolean} singleSelect
35715      * True to allow selection of only one row at a time (defaults to false)
35716      */
35717     singleSelect : false,
35718
35719     // private
35720     initEvents : function(){
35721
35722         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
35723             this.grid.on("mousedown", this.handleMouseDown, this);
35724         }else{ // allow click to work like normal
35725             this.grid.on("rowclick", this.handleDragableRowClick, this);
35726         }
35727
35728         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
35729             "up" : function(e){
35730                 if(!e.shiftKey){
35731                     this.selectPrevious(e.shiftKey);
35732                 }else if(this.last !== false && this.lastActive !== false){
35733                     var last = this.last;
35734                     this.selectRange(this.last,  this.lastActive-1);
35735                     this.grid.getView().focusRow(this.lastActive);
35736                     if(last !== false){
35737                         this.last = last;
35738                     }
35739                 }else{
35740                     this.selectFirstRow();
35741                 }
35742                 this.fireEvent("afterselectionchange", this);
35743             },
35744             "down" : function(e){
35745                 if(!e.shiftKey){
35746                     this.selectNext(e.shiftKey);
35747                 }else if(this.last !== false && this.lastActive !== false){
35748                     var last = this.last;
35749                     this.selectRange(this.last,  this.lastActive+1);
35750                     this.grid.getView().focusRow(this.lastActive);
35751                     if(last !== false){
35752                         this.last = last;
35753                     }
35754                 }else{
35755                     this.selectFirstRow();
35756                 }
35757                 this.fireEvent("afterselectionchange", this);
35758             },
35759             scope: this
35760         });
35761
35762         var view = this.grid.view;
35763         view.on("refresh", this.onRefresh, this);
35764         view.on("rowupdated", this.onRowUpdated, this);
35765         view.on("rowremoved", this.onRemove, this);
35766     },
35767
35768     // private
35769     onRefresh : function(){
35770         var ds = this.grid.dataSource, i, v = this.grid.view;
35771         var s = this.selections;
35772         s.each(function(r){
35773             if((i = ds.indexOfId(r.id)) != -1){
35774                 v.onRowSelect(i);
35775                 s.add(ds.getAt(i)); // updating the selection relate data
35776             }else{
35777                 s.remove(r);
35778             }
35779         });
35780     },
35781
35782     // private
35783     onRemove : function(v, index, r){
35784         this.selections.remove(r);
35785     },
35786
35787     // private
35788     onRowUpdated : function(v, index, r){
35789         if(this.isSelected(r)){
35790             v.onRowSelect(index);
35791         }
35792     },
35793
35794     /**
35795      * Select records.
35796      * @param {Array} records The records to select
35797      * @param {Boolean} keepExisting (optional) True to keep existing selections
35798      */
35799     selectRecords : function(records, keepExisting){
35800         if(!keepExisting){
35801             this.clearSelections();
35802         }
35803         var ds = this.grid.dataSource;
35804         for(var i = 0, len = records.length; i < len; i++){
35805             this.selectRow(ds.indexOf(records[i]), true);
35806         }
35807     },
35808
35809     /**
35810      * Gets the number of selected rows.
35811      * @return {Number}
35812      */
35813     getCount : function(){
35814         return this.selections.length;
35815     },
35816
35817     /**
35818      * Selects the first row in the grid.
35819      */
35820     selectFirstRow : function(){
35821         this.selectRow(0);
35822     },
35823
35824     /**
35825      * Select the last row.
35826      * @param {Boolean} keepExisting (optional) True to keep existing selections
35827      */
35828     selectLastRow : function(keepExisting){
35829         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
35830     },
35831
35832     /**
35833      * Selects the row immediately following the last selected row.
35834      * @param {Boolean} keepExisting (optional) True to keep existing selections
35835      */
35836     selectNext : function(keepExisting){
35837         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
35838             this.selectRow(this.last+1, keepExisting);
35839             this.grid.getView().focusRow(this.last);
35840         }
35841     },
35842
35843     /**
35844      * Selects the row that precedes the last selected row.
35845      * @param {Boolean} keepExisting (optional) True to keep existing selections
35846      */
35847     selectPrevious : function(keepExisting){
35848         if(this.last){
35849             this.selectRow(this.last-1, keepExisting);
35850             this.grid.getView().focusRow(this.last);
35851         }
35852     },
35853
35854     /**
35855      * Returns the selected records
35856      * @return {Array} Array of selected records
35857      */
35858     getSelections : function(){
35859         return [].concat(this.selections.items);
35860     },
35861
35862     /**
35863      * Returns the first selected record.
35864      * @return {Record}
35865      */
35866     getSelected : function(){
35867         return this.selections.itemAt(0);
35868     },
35869
35870
35871     /**
35872      * Clears all selections.
35873      */
35874     clearSelections : function(fast){
35875         if(this.locked) {
35876             return;
35877         }
35878         if(fast !== true){
35879             var ds = this.grid.dataSource;
35880             var s = this.selections;
35881             s.each(function(r){
35882                 this.deselectRow(ds.indexOfId(r.id));
35883             }, this);
35884             s.clear();
35885         }else{
35886             this.selections.clear();
35887         }
35888         this.last = false;
35889     },
35890
35891
35892     /**
35893      * Selects all rows.
35894      */
35895     selectAll : function(){
35896         if(this.locked) {
35897             return;
35898         }
35899         this.selections.clear();
35900         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
35901             this.selectRow(i, true);
35902         }
35903     },
35904
35905     /**
35906      * Returns True if there is a selection.
35907      * @return {Boolean}
35908      */
35909     hasSelection : function(){
35910         return this.selections.length > 0;
35911     },
35912
35913     /**
35914      * Returns True if the specified row is selected.
35915      * @param {Number/Record} record The record or index of the record to check
35916      * @return {Boolean}
35917      */
35918     isSelected : function(index){
35919         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
35920         return (r && this.selections.key(r.id) ? true : false);
35921     },
35922
35923     /**
35924      * Returns True if the specified record id is selected.
35925      * @param {String} id The id of record to check
35926      * @return {Boolean}
35927      */
35928     isIdSelected : function(id){
35929         return (this.selections.key(id) ? true : false);
35930     },
35931
35932     // private
35933     handleMouseDown : function(e, t){
35934         var view = this.grid.getView(), rowIndex;
35935         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
35936             return;
35937         };
35938         if(e.shiftKey && this.last !== false){
35939             var last = this.last;
35940             this.selectRange(last, rowIndex, e.ctrlKey);
35941             this.last = last; // reset the last
35942             view.focusRow(rowIndex);
35943         }else{
35944             var isSelected = this.isSelected(rowIndex);
35945             if(e.button !== 0 && isSelected){
35946                 view.focusRow(rowIndex);
35947             }else if(e.ctrlKey && isSelected){
35948                 this.deselectRow(rowIndex);
35949             }else if(!isSelected){
35950                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
35951                 view.focusRow(rowIndex);
35952             }
35953         }
35954         this.fireEvent("afterselectionchange", this);
35955     },
35956     // private
35957     handleDragableRowClick :  function(grid, rowIndex, e) 
35958     {
35959         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
35960             this.selectRow(rowIndex, false);
35961             grid.view.focusRow(rowIndex);
35962              this.fireEvent("afterselectionchange", this);
35963         }
35964     },
35965     
35966     /**
35967      * Selects multiple rows.
35968      * @param {Array} rows Array of the indexes of the row to select
35969      * @param {Boolean} keepExisting (optional) True to keep existing selections
35970      */
35971     selectRows : function(rows, keepExisting){
35972         if(!keepExisting){
35973             this.clearSelections();
35974         }
35975         for(var i = 0, len = rows.length; i < len; i++){
35976             this.selectRow(rows[i], true);
35977         }
35978     },
35979
35980     /**
35981      * Selects a range of rows. All rows in between startRow and endRow are also selected.
35982      * @param {Number} startRow The index of the first row in the range
35983      * @param {Number} endRow The index of the last row in the range
35984      * @param {Boolean} keepExisting (optional) True to retain existing selections
35985      */
35986     selectRange : function(startRow, endRow, keepExisting){
35987         if(this.locked) {
35988             return;
35989         }
35990         if(!keepExisting){
35991             this.clearSelections();
35992         }
35993         if(startRow <= endRow){
35994             for(var i = startRow; i <= endRow; i++){
35995                 this.selectRow(i, true);
35996             }
35997         }else{
35998             for(var i = startRow; i >= endRow; i--){
35999                 this.selectRow(i, true);
36000             }
36001         }
36002     },
36003
36004     /**
36005      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
36006      * @param {Number} startRow The index of the first row in the range
36007      * @param {Number} endRow The index of the last row in the range
36008      */
36009     deselectRange : function(startRow, endRow, preventViewNotify){
36010         if(this.locked) {
36011             return;
36012         }
36013         for(var i = startRow; i <= endRow; i++){
36014             this.deselectRow(i, preventViewNotify);
36015         }
36016     },
36017
36018     /**
36019      * Selects a row.
36020      * @param {Number} row The index of the row to select
36021      * @param {Boolean} keepExisting (optional) True to keep existing selections
36022      */
36023     selectRow : function(index, keepExisting, preventViewNotify){
36024         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
36025             return;
36026         }
36027         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
36028             if(!keepExisting || this.singleSelect){
36029                 this.clearSelections();
36030             }
36031             var r = this.grid.dataSource.getAt(index);
36032             this.selections.add(r);
36033             this.last = this.lastActive = index;
36034             if(!preventViewNotify){
36035                 this.grid.getView().onRowSelect(index);
36036             }
36037             this.fireEvent("rowselect", this, index, r);
36038             this.fireEvent("selectionchange", this);
36039         }
36040     },
36041
36042     /**
36043      * Deselects a row.
36044      * @param {Number} row The index of the row to deselect
36045      */
36046     deselectRow : function(index, preventViewNotify){
36047         if(this.locked) {
36048             return;
36049         }
36050         if(this.last == index){
36051             this.last = false;
36052         }
36053         if(this.lastActive == index){
36054             this.lastActive = false;
36055         }
36056         var r = this.grid.dataSource.getAt(index);
36057         this.selections.remove(r);
36058         if(!preventViewNotify){
36059             this.grid.getView().onRowDeselect(index);
36060         }
36061         this.fireEvent("rowdeselect", this, index);
36062         this.fireEvent("selectionchange", this);
36063     },
36064
36065     // private
36066     restoreLast : function(){
36067         if(this._last){
36068             this.last = this._last;
36069         }
36070     },
36071
36072     // private
36073     acceptsNav : function(row, col, cm){
36074         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36075     },
36076
36077     // private
36078     onEditorKey : function(field, e){
36079         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
36080         if(k == e.TAB){
36081             e.stopEvent();
36082             ed.completeEdit();
36083             if(e.shiftKey){
36084                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36085             }else{
36086                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36087             }
36088         }else if(k == e.ENTER && !e.ctrlKey){
36089             e.stopEvent();
36090             ed.completeEdit();
36091             if(e.shiftKey){
36092                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
36093             }else{
36094                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
36095             }
36096         }else if(k == e.ESC){
36097             ed.cancelEdit();
36098         }
36099         if(newCell){
36100             g.startEditing(newCell[0], newCell[1]);
36101         }
36102     }
36103 });/*
36104  * Based on:
36105  * Ext JS Library 1.1.1
36106  * Copyright(c) 2006-2007, Ext JS, LLC.
36107  *
36108  * Originally Released Under LGPL - original licence link has changed is not relivant.
36109  *
36110  * Fork - LGPL
36111  * <script type="text/javascript">
36112  */
36113 /**
36114  * @class Roo.grid.CellSelectionModel
36115  * @extends Roo.grid.AbstractSelectionModel
36116  * This class provides the basic implementation for cell selection in a grid.
36117  * @constructor
36118  * @param {Object} config The object containing the configuration of this model.
36119  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
36120  */
36121 Roo.grid.CellSelectionModel = function(config){
36122     Roo.apply(this, config);
36123
36124     this.selection = null;
36125
36126     this.addEvents({
36127         /**
36128              * @event beforerowselect
36129              * Fires before a cell is selected.
36130              * @param {SelectionModel} this
36131              * @param {Number} rowIndex The selected row index
36132              * @param {Number} colIndex The selected cell index
36133              */
36134             "beforecellselect" : true,
36135         /**
36136              * @event cellselect
36137              * Fires when a cell is selected.
36138              * @param {SelectionModel} this
36139              * @param {Number} rowIndex The selected row index
36140              * @param {Number} colIndex The selected cell index
36141              */
36142             "cellselect" : true,
36143         /**
36144              * @event selectionchange
36145              * Fires when the active selection changes.
36146              * @param {SelectionModel} this
36147              * @param {Object} selection null for no selection or an object (o) with two properties
36148                 <ul>
36149                 <li>o.record: the record object for the row the selection is in</li>
36150                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
36151                 </ul>
36152              */
36153             "selectionchange" : true,
36154         /**
36155              * @event tabend
36156              * Fires when the tab (or enter) was pressed on the last editable cell
36157              * You can use this to trigger add new row.
36158              * @param {SelectionModel} this
36159              */
36160             "tabend" : true,
36161          /**
36162              * @event beforeeditnext
36163              * Fires before the next editable sell is made active
36164              * You can use this to skip to another cell or fire the tabend
36165              *    if you set cell to false
36166              * @param {Object} eventdata object : { cell : [ row, col ] } 
36167              */
36168             "beforeeditnext" : true
36169     });
36170     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
36171 };
36172
36173 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
36174     
36175     enter_is_tab: false,
36176
36177     /** @ignore */
36178     initEvents : function(){
36179         this.grid.on("mousedown", this.handleMouseDown, this);
36180         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
36181         var view = this.grid.view;
36182         view.on("refresh", this.onViewChange, this);
36183         view.on("rowupdated", this.onRowUpdated, this);
36184         view.on("beforerowremoved", this.clearSelections, this);
36185         view.on("beforerowsinserted", this.clearSelections, this);
36186         if(this.grid.isEditor){
36187             this.grid.on("beforeedit", this.beforeEdit,  this);
36188         }
36189     },
36190
36191         //private
36192     beforeEdit : function(e){
36193         this.select(e.row, e.column, false, true, e.record);
36194     },
36195
36196         //private
36197     onRowUpdated : function(v, index, r){
36198         if(this.selection && this.selection.record == r){
36199             v.onCellSelect(index, this.selection.cell[1]);
36200         }
36201     },
36202
36203         //private
36204     onViewChange : function(){
36205         this.clearSelections(true);
36206     },
36207
36208         /**
36209          * Returns the currently selected cell,.
36210          * @return {Array} The selected cell (row, column) or null if none selected.
36211          */
36212     getSelectedCell : function(){
36213         return this.selection ? this.selection.cell : null;
36214     },
36215
36216     /**
36217      * Clears all selections.
36218      * @param {Boolean} true to prevent the gridview from being notified about the change.
36219      */
36220     clearSelections : function(preventNotify){
36221         var s = this.selection;
36222         if(s){
36223             if(preventNotify !== true){
36224                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
36225             }
36226             this.selection = null;
36227             this.fireEvent("selectionchange", this, null);
36228         }
36229     },
36230
36231     /**
36232      * Returns true if there is a selection.
36233      * @return {Boolean}
36234      */
36235     hasSelection : function(){
36236         return this.selection ? true : false;
36237     },
36238
36239     /** @ignore */
36240     handleMouseDown : function(e, t){
36241         var v = this.grid.getView();
36242         if(this.isLocked()){
36243             return;
36244         };
36245         var row = v.findRowIndex(t);
36246         var cell = v.findCellIndex(t);
36247         if(row !== false && cell !== false){
36248             this.select(row, cell);
36249         }
36250     },
36251
36252     /**
36253      * Selects a cell.
36254      * @param {Number} rowIndex
36255      * @param {Number} collIndex
36256      */
36257     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
36258         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
36259             this.clearSelections();
36260             r = r || this.grid.dataSource.getAt(rowIndex);
36261             this.selection = {
36262                 record : r,
36263                 cell : [rowIndex, colIndex]
36264             };
36265             if(!preventViewNotify){
36266                 var v = this.grid.getView();
36267                 v.onCellSelect(rowIndex, colIndex);
36268                 if(preventFocus !== true){
36269                     v.focusCell(rowIndex, colIndex);
36270                 }
36271             }
36272             this.fireEvent("cellselect", this, rowIndex, colIndex);
36273             this.fireEvent("selectionchange", this, this.selection);
36274         }
36275     },
36276
36277         //private
36278     isSelectable : function(rowIndex, colIndex, cm){
36279         return !cm.isHidden(colIndex);
36280     },
36281
36282     /** @ignore */
36283     handleKeyDown : function(e){
36284         //Roo.log('Cell Sel Model handleKeyDown');
36285         if(!e.isNavKeyPress()){
36286             return;
36287         }
36288         var g = this.grid, s = this.selection;
36289         if(!s){
36290             e.stopEvent();
36291             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
36292             if(cell){
36293                 this.select(cell[0], cell[1]);
36294             }
36295             return;
36296         }
36297         var sm = this;
36298         var walk = function(row, col, step){
36299             return g.walkCells(row, col, step, sm.isSelectable,  sm);
36300         };
36301         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
36302         var newCell;
36303
36304       
36305
36306         switch(k){
36307             case e.TAB:
36308                 // handled by onEditorKey
36309                 if (g.isEditor && g.editing) {
36310                     return;
36311                 }
36312                 if(e.shiftKey) {
36313                     newCell = walk(r, c-1, -1);
36314                 } else {
36315                     newCell = walk(r, c+1, 1);
36316                 }
36317                 break;
36318             
36319             case e.DOWN:
36320                newCell = walk(r+1, c, 1);
36321                 break;
36322             
36323             case e.UP:
36324                 newCell = walk(r-1, c, -1);
36325                 break;
36326             
36327             case e.RIGHT:
36328                 newCell = walk(r, c+1, 1);
36329                 break;
36330             
36331             case e.LEFT:
36332                 newCell = walk(r, c-1, -1);
36333                 break;
36334             
36335             case e.ENTER:
36336                 
36337                 if(g.isEditor && !g.editing){
36338                    g.startEditing(r, c);
36339                    e.stopEvent();
36340                    return;
36341                 }
36342                 
36343                 
36344              break;
36345         };
36346         if(newCell){
36347             this.select(newCell[0], newCell[1]);
36348             e.stopEvent();
36349             
36350         }
36351     },
36352
36353     acceptsNav : function(row, col, cm){
36354         return !cm.isHidden(col) && cm.isCellEditable(col, row);
36355     },
36356     /**
36357      * Selects a cell.
36358      * @param {Number} field (not used) - as it's normally used as a listener
36359      * @param {Number} e - event - fake it by using
36360      *
36361      * var e = Roo.EventObjectImpl.prototype;
36362      * e.keyCode = e.TAB
36363      *
36364      * 
36365      */
36366     onEditorKey : function(field, e){
36367         
36368         var k = e.getKey(),
36369             newCell,
36370             g = this.grid,
36371             ed = g.activeEditor,
36372             forward = false;
36373         ///Roo.log('onEditorKey' + k);
36374         
36375         
36376         if (this.enter_is_tab && k == e.ENTER) {
36377             k = e.TAB;
36378         }
36379         
36380         if(k == e.TAB){
36381             if(e.shiftKey){
36382                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
36383             }else{
36384                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36385                 forward = true;
36386             }
36387             
36388             e.stopEvent();
36389             
36390         } else if(k == e.ENTER &&  !e.ctrlKey){
36391             ed.completeEdit();
36392             e.stopEvent();
36393             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
36394         
36395                 } else if(k == e.ESC){
36396             ed.cancelEdit();
36397         }
36398                 
36399         if (newCell) {
36400             var ecall = { cell : newCell, forward : forward };
36401             this.fireEvent('beforeeditnext', ecall );
36402             newCell = ecall.cell;
36403                         forward = ecall.forward;
36404         }
36405                 
36406         if(newCell){
36407             //Roo.log('next cell after edit');
36408             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
36409         } else if (forward) {
36410             // tabbed past last
36411             this.fireEvent.defer(100, this, ['tabend',this]);
36412         }
36413     }
36414 });/*
36415  * Based on:
36416  * Ext JS Library 1.1.1
36417  * Copyright(c) 2006-2007, Ext JS, LLC.
36418  *
36419  * Originally Released Under LGPL - original licence link has changed is not relivant.
36420  *
36421  * Fork - LGPL
36422  * <script type="text/javascript">
36423  */
36424  
36425 /**
36426  * @class Roo.grid.EditorGrid
36427  * @extends Roo.grid.Grid
36428  * Class for creating and editable grid.
36429  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
36430  * The container MUST have some type of size defined for the grid to fill. The container will be 
36431  * automatically set to position relative if it isn't already.
36432  * @param {Object} dataSource The data model to bind to
36433  * @param {Object} colModel The column model with info about this grid's columns
36434  */
36435 Roo.grid.EditorGrid = function(container, config){
36436     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
36437     this.getGridEl().addClass("xedit-grid");
36438
36439     if(!this.selModel){
36440         this.selModel = new Roo.grid.CellSelectionModel();
36441     }
36442
36443     this.activeEditor = null;
36444
36445         this.addEvents({
36446             /**
36447              * @event beforeedit
36448              * Fires before cell editing is triggered. The edit event object has the following properties <br />
36449              * <ul style="padding:5px;padding-left:16px;">
36450              * <li>grid - This grid</li>
36451              * <li>record - The record being edited</li>
36452              * <li>field - The field name being edited</li>
36453              * <li>value - The value for the field being edited.</li>
36454              * <li>row - The grid row index</li>
36455              * <li>column - The grid column index</li>
36456              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36457              * </ul>
36458              * @param {Object} e An edit event (see above for description)
36459              */
36460             "beforeedit" : true,
36461             /**
36462              * @event afteredit
36463              * Fires after a cell is edited. <br />
36464              * <ul style="padding:5px;padding-left:16px;">
36465              * <li>grid - This grid</li>
36466              * <li>record - The record being edited</li>
36467              * <li>field - The field name being edited</li>
36468              * <li>value - The value being set</li>
36469              * <li>originalValue - The original value for the field, before the edit.</li>
36470              * <li>row - The grid row index</li>
36471              * <li>column - The grid column index</li>
36472              * </ul>
36473              * @param {Object} e An edit event (see above for description)
36474              */
36475             "afteredit" : true,
36476             /**
36477              * @event validateedit
36478              * Fires after a cell is edited, but before the value is set in the record. 
36479          * You can use this to modify the value being set in the field, Return false
36480              * to cancel the change. The edit event object has the following properties <br />
36481              * <ul style="padding:5px;padding-left:16px;">
36482          * <li>editor - This editor</li>
36483              * <li>grid - This grid</li>
36484              * <li>record - The record being edited</li>
36485              * <li>field - The field name being edited</li>
36486              * <li>value - The value being set</li>
36487              * <li>originalValue - The original value for the field, before the edit.</li>
36488              * <li>row - The grid row index</li>
36489              * <li>column - The grid column index</li>
36490              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
36491              * </ul>
36492              * @param {Object} e An edit event (see above for description)
36493              */
36494             "validateedit" : true
36495         });
36496     this.on("bodyscroll", this.stopEditing,  this);
36497     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
36498 };
36499
36500 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
36501     /**
36502      * @cfg {Number} clicksToEdit
36503      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
36504      */
36505     clicksToEdit: 2,
36506
36507     // private
36508     isEditor : true,
36509     // private
36510     trackMouseOver: false, // causes very odd FF errors
36511
36512     onCellDblClick : function(g, row, col){
36513         this.startEditing(row, col);
36514     },
36515
36516     onEditComplete : function(ed, value, startValue){
36517         this.editing = false;
36518         this.activeEditor = null;
36519         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
36520         var r = ed.record;
36521         var field = this.colModel.getDataIndex(ed.col);
36522         var e = {
36523             grid: this,
36524             record: r,
36525             field: field,
36526             originalValue: startValue,
36527             value: value,
36528             row: ed.row,
36529             column: ed.col,
36530             cancel:false,
36531             editor: ed
36532         };
36533         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
36534         cell.show();
36535           
36536         if(String(value) !== String(startValue)){
36537             
36538             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
36539                 r.set(field, e.value);
36540                 // if we are dealing with a combo box..
36541                 // then we also set the 'name' colum to be the displayField
36542                 if (ed.field.displayField && ed.field.name) {
36543                     r.set(ed.field.name, ed.field.el.dom.value);
36544                 }
36545                 
36546                 delete e.cancel; //?? why!!!
36547                 this.fireEvent("afteredit", e);
36548             }
36549         } else {
36550             this.fireEvent("afteredit", e); // always fire it!
36551         }
36552         this.view.focusCell(ed.row, ed.col);
36553     },
36554
36555     /**
36556      * Starts editing the specified for the specified row/column
36557      * @param {Number} rowIndex
36558      * @param {Number} colIndex
36559      */
36560     startEditing : function(row, col){
36561         this.stopEditing();
36562         if(this.colModel.isCellEditable(col, row)){
36563             this.view.ensureVisible(row, col, true);
36564           
36565             var r = this.dataSource.getAt(row);
36566             var field = this.colModel.getDataIndex(col);
36567             var cell = Roo.get(this.view.getCell(row,col));
36568             var e = {
36569                 grid: this,
36570                 record: r,
36571                 field: field,
36572                 value: r.data[field],
36573                 row: row,
36574                 column: col,
36575                 cancel:false 
36576             };
36577             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
36578                 this.editing = true;
36579                 var ed = this.colModel.getCellEditor(col, row);
36580                 
36581                 if (!ed) {
36582                     return;
36583                 }
36584                 if(!ed.rendered){
36585                     ed.render(ed.parentEl || document.body);
36586                 }
36587                 ed.field.reset();
36588                
36589                 cell.hide();
36590                 
36591                 (function(){ // complex but required for focus issues in safari, ie and opera
36592                     ed.row = row;
36593                     ed.col = col;
36594                     ed.record = r;
36595                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
36596                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
36597                     this.activeEditor = ed;
36598                     var v = r.data[field];
36599                     ed.startEdit(this.view.getCell(row, col), v);
36600                     // combo's with 'displayField and name set
36601                     if (ed.field.displayField && ed.field.name) {
36602                         ed.field.el.dom.value = r.data[ed.field.name];
36603                     }
36604                     
36605                     
36606                 }).defer(50, this);
36607             }
36608         }
36609     },
36610         
36611     /**
36612      * Stops any active editing
36613      */
36614     stopEditing : function(){
36615         if(this.activeEditor){
36616             this.activeEditor.completeEdit();
36617         }
36618         this.activeEditor = null;
36619     },
36620         
36621          /**
36622      * Called to get grid's drag proxy text, by default returns this.ddText.
36623      * @return {String}
36624      */
36625     getDragDropText : function(){
36626         var count = this.selModel.getSelectedCell() ? 1 : 0;
36627         return String.format(this.ddText, count, count == 1 ? '' : 's');
36628     }
36629         
36630 });/*
36631  * Based on:
36632  * Ext JS Library 1.1.1
36633  * Copyright(c) 2006-2007, Ext JS, LLC.
36634  *
36635  * Originally Released Under LGPL - original licence link has changed is not relivant.
36636  *
36637  * Fork - LGPL
36638  * <script type="text/javascript">
36639  */
36640
36641 // private - not really -- you end up using it !
36642 // This is a support class used internally by the Grid components
36643
36644 /**
36645  * @class Roo.grid.GridEditor
36646  * @extends Roo.Editor
36647  * Class for creating and editable grid elements.
36648  * @param {Object} config any settings (must include field)
36649  */
36650 Roo.grid.GridEditor = function(field, config){
36651     if (!config && field.field) {
36652         config = field;
36653         field = Roo.factory(config.field, Roo.form);
36654     }
36655     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
36656     field.monitorTab = false;
36657 };
36658
36659 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
36660     
36661     /**
36662      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
36663      */
36664     
36665     alignment: "tl-tl",
36666     autoSize: "width",
36667     hideEl : false,
36668     cls: "x-small-editor x-grid-editor",
36669     shim:false,
36670     shadow:"frame"
36671 });/*
36672  * Based on:
36673  * Ext JS Library 1.1.1
36674  * Copyright(c) 2006-2007, Ext JS, LLC.
36675  *
36676  * Originally Released Under LGPL - original licence link has changed is not relivant.
36677  *
36678  * Fork - LGPL
36679  * <script type="text/javascript">
36680  */
36681   
36682
36683   
36684 Roo.grid.PropertyRecord = Roo.data.Record.create([
36685     {name:'name',type:'string'},  'value'
36686 ]);
36687
36688
36689 Roo.grid.PropertyStore = function(grid, source){
36690     this.grid = grid;
36691     this.store = new Roo.data.Store({
36692         recordType : Roo.grid.PropertyRecord
36693     });
36694     this.store.on('update', this.onUpdate,  this);
36695     if(source){
36696         this.setSource(source);
36697     }
36698     Roo.grid.PropertyStore.superclass.constructor.call(this);
36699 };
36700
36701
36702
36703 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
36704     setSource : function(o){
36705         this.source = o;
36706         this.store.removeAll();
36707         var data = [];
36708         for(var k in o){
36709             if(this.isEditableValue(o[k])){
36710                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
36711             }
36712         }
36713         this.store.loadRecords({records: data}, {}, true);
36714     },
36715
36716     onUpdate : function(ds, record, type){
36717         if(type == Roo.data.Record.EDIT){
36718             var v = record.data['value'];
36719             var oldValue = record.modified['value'];
36720             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
36721                 this.source[record.id] = v;
36722                 record.commit();
36723                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
36724             }else{
36725                 record.reject();
36726             }
36727         }
36728     },
36729
36730     getProperty : function(row){
36731        return this.store.getAt(row);
36732     },
36733
36734     isEditableValue: function(val){
36735         if(val && val instanceof Date){
36736             return true;
36737         }else if(typeof val == 'object' || typeof val == 'function'){
36738             return false;
36739         }
36740         return true;
36741     },
36742
36743     setValue : function(prop, value){
36744         this.source[prop] = value;
36745         this.store.getById(prop).set('value', value);
36746     },
36747
36748     getSource : function(){
36749         return this.source;
36750     }
36751 });
36752
36753 Roo.grid.PropertyColumnModel = function(grid, store){
36754     this.grid = grid;
36755     var g = Roo.grid;
36756     g.PropertyColumnModel.superclass.constructor.call(this, [
36757         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
36758         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
36759     ]);
36760     this.store = store;
36761     this.bselect = Roo.DomHelper.append(document.body, {
36762         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
36763             {tag: 'option', value: 'true', html: 'true'},
36764             {tag: 'option', value: 'false', html: 'false'}
36765         ]
36766     });
36767     Roo.id(this.bselect);
36768     var f = Roo.form;
36769     this.editors = {
36770         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
36771         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
36772         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
36773         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
36774         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
36775     };
36776     this.renderCellDelegate = this.renderCell.createDelegate(this);
36777     this.renderPropDelegate = this.renderProp.createDelegate(this);
36778 };
36779
36780 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
36781     
36782     
36783     nameText : 'Name',
36784     valueText : 'Value',
36785     
36786     dateFormat : 'm/j/Y',
36787     
36788     
36789     renderDate : function(dateVal){
36790         return dateVal.dateFormat(this.dateFormat);
36791     },
36792
36793     renderBool : function(bVal){
36794         return bVal ? 'true' : 'false';
36795     },
36796
36797     isCellEditable : function(colIndex, rowIndex){
36798         return colIndex == 1;
36799     },
36800
36801     getRenderer : function(col){
36802         return col == 1 ?
36803             this.renderCellDelegate : this.renderPropDelegate;
36804     },
36805
36806     renderProp : function(v){
36807         return this.getPropertyName(v);
36808     },
36809
36810     renderCell : function(val){
36811         var rv = val;
36812         if(val instanceof Date){
36813             rv = this.renderDate(val);
36814         }else if(typeof val == 'boolean'){
36815             rv = this.renderBool(val);
36816         }
36817         return Roo.util.Format.htmlEncode(rv);
36818     },
36819
36820     getPropertyName : function(name){
36821         var pn = this.grid.propertyNames;
36822         return pn && pn[name] ? pn[name] : name;
36823     },
36824
36825     getCellEditor : function(colIndex, rowIndex){
36826         var p = this.store.getProperty(rowIndex);
36827         var n = p.data['name'], val = p.data['value'];
36828         
36829         if(typeof(this.grid.customEditors[n]) == 'string'){
36830             return this.editors[this.grid.customEditors[n]];
36831         }
36832         if(typeof(this.grid.customEditors[n]) != 'undefined'){
36833             return this.grid.customEditors[n];
36834         }
36835         if(val instanceof Date){
36836             return this.editors['date'];
36837         }else if(typeof val == 'number'){
36838             return this.editors['number'];
36839         }else if(typeof val == 'boolean'){
36840             return this.editors['boolean'];
36841         }else{
36842             return this.editors['string'];
36843         }
36844     }
36845 });
36846
36847 /**
36848  * @class Roo.grid.PropertyGrid
36849  * @extends Roo.grid.EditorGrid
36850  * This class represents the  interface of a component based property grid control.
36851  * <br><br>Usage:<pre><code>
36852  var grid = new Roo.grid.PropertyGrid("my-container-id", {
36853       
36854  });
36855  // set any options
36856  grid.render();
36857  * </code></pre>
36858   
36859  * @constructor
36860  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36861  * The container MUST have some type of size defined for the grid to fill. The container will be
36862  * automatically set to position relative if it isn't already.
36863  * @param {Object} config A config object that sets properties on this grid.
36864  */
36865 Roo.grid.PropertyGrid = function(container, config){
36866     config = config || {};
36867     var store = new Roo.grid.PropertyStore(this);
36868     this.store = store;
36869     var cm = new Roo.grid.PropertyColumnModel(this, store);
36870     store.store.sort('name', 'ASC');
36871     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
36872         ds: store.store,
36873         cm: cm,
36874         enableColLock:false,
36875         enableColumnMove:false,
36876         stripeRows:false,
36877         trackMouseOver: false,
36878         clicksToEdit:1
36879     }, config));
36880     this.getGridEl().addClass('x-props-grid');
36881     this.lastEditRow = null;
36882     this.on('columnresize', this.onColumnResize, this);
36883     this.addEvents({
36884          /**
36885              * @event beforepropertychange
36886              * Fires before a property changes (return false to stop?)
36887              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36888              * @param {String} id Record Id
36889              * @param {String} newval New Value
36890          * @param {String} oldval Old Value
36891              */
36892         "beforepropertychange": true,
36893         /**
36894              * @event propertychange
36895              * Fires after a property changes
36896              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
36897              * @param {String} id Record Id
36898              * @param {String} newval New Value
36899          * @param {String} oldval Old Value
36900              */
36901         "propertychange": true
36902     });
36903     this.customEditors = this.customEditors || {};
36904 };
36905 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
36906     
36907      /**
36908      * @cfg {Object} customEditors map of colnames=> custom editors.
36909      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
36910      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
36911      * false disables editing of the field.
36912          */
36913     
36914       /**
36915      * @cfg {Object} propertyNames map of property Names to their displayed value
36916          */
36917     
36918     render : function(){
36919         Roo.grid.PropertyGrid.superclass.render.call(this);
36920         this.autoSize.defer(100, this);
36921     },
36922
36923     autoSize : function(){
36924         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
36925         if(this.view){
36926             this.view.fitColumns();
36927         }
36928     },
36929
36930     onColumnResize : function(){
36931         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
36932         this.autoSize();
36933     },
36934     /**
36935      * Sets the data for the Grid
36936      * accepts a Key => Value object of all the elements avaiable.
36937      * @param {Object} data  to appear in grid.
36938      */
36939     setSource : function(source){
36940         this.store.setSource(source);
36941         //this.autoSize();
36942     },
36943     /**
36944      * Gets all the data from the grid.
36945      * @return {Object} data  data stored in grid
36946      */
36947     getSource : function(){
36948         return this.store.getSource();
36949     }
36950 });/*
36951   
36952  * Licence LGPL
36953  
36954  */
36955  
36956 /**
36957  * @class Roo.grid.Calendar
36958  * @extends Roo.util.Grid
36959  * This class extends the Grid to provide a calendar widget
36960  * <br><br>Usage:<pre><code>
36961  var grid = new Roo.grid.Calendar("my-container-id", {
36962      ds: myDataStore,
36963      cm: myColModel,
36964      selModel: mySelectionModel,
36965      autoSizeColumns: true,
36966      monitorWindowResize: false,
36967      trackMouseOver: true
36968      eventstore : real data store..
36969  });
36970  // set any options
36971  grid.render();
36972   
36973   * @constructor
36974  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
36975  * The container MUST have some type of size defined for the grid to fill. The container will be
36976  * automatically set to position relative if it isn't already.
36977  * @param {Object} config A config object that sets properties on this grid.
36978  */
36979 Roo.grid.Calendar = function(container, config){
36980         // initialize the container
36981         this.container = Roo.get(container);
36982         this.container.update("");
36983         this.container.setStyle("overflow", "hidden");
36984     this.container.addClass('x-grid-container');
36985
36986     this.id = this.container.id;
36987
36988     Roo.apply(this, config);
36989     // check and correct shorthanded configs
36990     
36991     var rows = [];
36992     var d =1;
36993     for (var r = 0;r < 6;r++) {
36994         
36995         rows[r]=[];
36996         for (var c =0;c < 7;c++) {
36997             rows[r][c]= '';
36998         }
36999     }
37000     if (this.eventStore) {
37001         this.eventStore= Roo.factory(this.eventStore, Roo.data);
37002         this.eventStore.on('load',this.onLoad, this);
37003         this.eventStore.on('beforeload',this.clearEvents, this);
37004          
37005     }
37006     
37007     this.dataSource = new Roo.data.Store({
37008             proxy: new Roo.data.MemoryProxy(rows),
37009             reader: new Roo.data.ArrayReader({}, [
37010                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
37011     });
37012
37013     this.dataSource.load();
37014     this.ds = this.dataSource;
37015     this.ds.xmodule = this.xmodule || false;
37016     
37017     
37018     var cellRender = function(v,x,r)
37019     {
37020         return String.format(
37021             '<div class="fc-day  fc-widget-content"><div>' +
37022                 '<div class="fc-event-container"></div>' +
37023                 '<div class="fc-day-number">{0}</div>'+
37024                 
37025                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
37026             '</div></div>', v);
37027     
37028     }
37029     
37030     
37031     this.colModel = new Roo.grid.ColumnModel( [
37032         {
37033             xtype: 'ColumnModel',
37034             xns: Roo.grid,
37035             dataIndex : 'weekday0',
37036             header : 'Sunday',
37037             renderer : cellRender
37038         },
37039         {
37040             xtype: 'ColumnModel',
37041             xns: Roo.grid,
37042             dataIndex : 'weekday1',
37043             header : 'Monday',
37044             renderer : cellRender
37045         },
37046         {
37047             xtype: 'ColumnModel',
37048             xns: Roo.grid,
37049             dataIndex : 'weekday2',
37050             header : 'Tuesday',
37051             renderer : cellRender
37052         },
37053         {
37054             xtype: 'ColumnModel',
37055             xns: Roo.grid,
37056             dataIndex : 'weekday3',
37057             header : 'Wednesday',
37058             renderer : cellRender
37059         },
37060         {
37061             xtype: 'ColumnModel',
37062             xns: Roo.grid,
37063             dataIndex : 'weekday4',
37064             header : 'Thursday',
37065             renderer : cellRender
37066         },
37067         {
37068             xtype: 'ColumnModel',
37069             xns: Roo.grid,
37070             dataIndex : 'weekday5',
37071             header : 'Friday',
37072             renderer : cellRender
37073         },
37074         {
37075             xtype: 'ColumnModel',
37076             xns: Roo.grid,
37077             dataIndex : 'weekday6',
37078             header : 'Saturday',
37079             renderer : cellRender
37080         }
37081     ]);
37082     this.cm = this.colModel;
37083     this.cm.xmodule = this.xmodule || false;
37084  
37085         
37086           
37087     //this.selModel = new Roo.grid.CellSelectionModel();
37088     //this.sm = this.selModel;
37089     //this.selModel.init(this);
37090     
37091     
37092     if(this.width){
37093         this.container.setWidth(this.width);
37094     }
37095
37096     if(this.height){
37097         this.container.setHeight(this.height);
37098     }
37099     /** @private */
37100         this.addEvents({
37101         // raw events
37102         /**
37103          * @event click
37104          * The raw click event for the entire grid.
37105          * @param {Roo.EventObject} e
37106          */
37107         "click" : true,
37108         /**
37109          * @event dblclick
37110          * The raw dblclick event for the entire grid.
37111          * @param {Roo.EventObject} e
37112          */
37113         "dblclick" : true,
37114         /**
37115          * @event contextmenu
37116          * The raw contextmenu event for the entire grid.
37117          * @param {Roo.EventObject} e
37118          */
37119         "contextmenu" : true,
37120         /**
37121          * @event mousedown
37122          * The raw mousedown event for the entire grid.
37123          * @param {Roo.EventObject} e
37124          */
37125         "mousedown" : true,
37126         /**
37127          * @event mouseup
37128          * The raw mouseup event for the entire grid.
37129          * @param {Roo.EventObject} e
37130          */
37131         "mouseup" : true,
37132         /**
37133          * @event mouseover
37134          * The raw mouseover event for the entire grid.
37135          * @param {Roo.EventObject} e
37136          */
37137         "mouseover" : true,
37138         /**
37139          * @event mouseout
37140          * The raw mouseout event for the entire grid.
37141          * @param {Roo.EventObject} e
37142          */
37143         "mouseout" : true,
37144         /**
37145          * @event keypress
37146          * The raw keypress event for the entire grid.
37147          * @param {Roo.EventObject} e
37148          */
37149         "keypress" : true,
37150         /**
37151          * @event keydown
37152          * The raw keydown event for the entire grid.
37153          * @param {Roo.EventObject} e
37154          */
37155         "keydown" : true,
37156
37157         // custom events
37158
37159         /**
37160          * @event cellclick
37161          * Fires when a cell is clicked
37162          * @param {Grid} this
37163          * @param {Number} rowIndex
37164          * @param {Number} columnIndex
37165          * @param {Roo.EventObject} e
37166          */
37167         "cellclick" : true,
37168         /**
37169          * @event celldblclick
37170          * Fires when a cell is double clicked
37171          * @param {Grid} this
37172          * @param {Number} rowIndex
37173          * @param {Number} columnIndex
37174          * @param {Roo.EventObject} e
37175          */
37176         "celldblclick" : true,
37177         /**
37178          * @event rowclick
37179          * Fires when a row is clicked
37180          * @param {Grid} this
37181          * @param {Number} rowIndex
37182          * @param {Roo.EventObject} e
37183          */
37184         "rowclick" : true,
37185         /**
37186          * @event rowdblclick
37187          * Fires when a row is double clicked
37188          * @param {Grid} this
37189          * @param {Number} rowIndex
37190          * @param {Roo.EventObject} e
37191          */
37192         "rowdblclick" : true,
37193         /**
37194          * @event headerclick
37195          * Fires when a header is clicked
37196          * @param {Grid} this
37197          * @param {Number} columnIndex
37198          * @param {Roo.EventObject} e
37199          */
37200         "headerclick" : true,
37201         /**
37202          * @event headerdblclick
37203          * Fires when a header cell is double clicked
37204          * @param {Grid} this
37205          * @param {Number} columnIndex
37206          * @param {Roo.EventObject} e
37207          */
37208         "headerdblclick" : true,
37209         /**
37210          * @event rowcontextmenu
37211          * Fires when a row is right clicked
37212          * @param {Grid} this
37213          * @param {Number} rowIndex
37214          * @param {Roo.EventObject} e
37215          */
37216         "rowcontextmenu" : true,
37217         /**
37218          * @event cellcontextmenu
37219          * Fires when a cell is right clicked
37220          * @param {Grid} this
37221          * @param {Number} rowIndex
37222          * @param {Number} cellIndex
37223          * @param {Roo.EventObject} e
37224          */
37225          "cellcontextmenu" : true,
37226         /**
37227          * @event headercontextmenu
37228          * Fires when a header is right clicked
37229          * @param {Grid} this
37230          * @param {Number} columnIndex
37231          * @param {Roo.EventObject} e
37232          */
37233         "headercontextmenu" : true,
37234         /**
37235          * @event bodyscroll
37236          * Fires when the body element is scrolled
37237          * @param {Number} scrollLeft
37238          * @param {Number} scrollTop
37239          */
37240         "bodyscroll" : true,
37241         /**
37242          * @event columnresize
37243          * Fires when the user resizes a column
37244          * @param {Number} columnIndex
37245          * @param {Number} newSize
37246          */
37247         "columnresize" : true,
37248         /**
37249          * @event columnmove
37250          * Fires when the user moves a column
37251          * @param {Number} oldIndex
37252          * @param {Number} newIndex
37253          */
37254         "columnmove" : true,
37255         /**
37256          * @event startdrag
37257          * Fires when row(s) start being dragged
37258          * @param {Grid} this
37259          * @param {Roo.GridDD} dd The drag drop object
37260          * @param {event} e The raw browser event
37261          */
37262         "startdrag" : true,
37263         /**
37264          * @event enddrag
37265          * Fires when a drag operation is complete
37266          * @param {Grid} this
37267          * @param {Roo.GridDD} dd The drag drop object
37268          * @param {event} e The raw browser event
37269          */
37270         "enddrag" : true,
37271         /**
37272          * @event dragdrop
37273          * Fires when dragged row(s) are dropped on a valid DD target
37274          * @param {Grid} this
37275          * @param {Roo.GridDD} dd The drag drop object
37276          * @param {String} targetId The target drag drop object
37277          * @param {event} e The raw browser event
37278          */
37279         "dragdrop" : true,
37280         /**
37281          * @event dragover
37282          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
37283          * @param {Grid} this
37284          * @param {Roo.GridDD} dd The drag drop object
37285          * @param {String} targetId The target drag drop object
37286          * @param {event} e The raw browser event
37287          */
37288         "dragover" : true,
37289         /**
37290          * @event dragenter
37291          *  Fires when the dragged row(s) first cross another DD target while being dragged
37292          * @param {Grid} this
37293          * @param {Roo.GridDD} dd The drag drop object
37294          * @param {String} targetId The target drag drop object
37295          * @param {event} e The raw browser event
37296          */
37297         "dragenter" : true,
37298         /**
37299          * @event dragout
37300          * Fires when the dragged row(s) leave another DD target while being dragged
37301          * @param {Grid} this
37302          * @param {Roo.GridDD} dd The drag drop object
37303          * @param {String} targetId The target drag drop object
37304          * @param {event} e The raw browser event
37305          */
37306         "dragout" : true,
37307         /**
37308          * @event rowclass
37309          * Fires when a row is rendered, so you can change add a style to it.
37310          * @param {GridView} gridview   The grid view
37311          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
37312          */
37313         'rowclass' : true,
37314
37315         /**
37316          * @event render
37317          * Fires when the grid is rendered
37318          * @param {Grid} grid
37319          */
37320         'render' : true,
37321             /**
37322              * @event select
37323              * Fires when a date is selected
37324              * @param {DatePicker} this
37325              * @param {Date} date The selected date
37326              */
37327         'select': true,
37328         /**
37329              * @event monthchange
37330              * Fires when the displayed month changes 
37331              * @param {DatePicker} this
37332              * @param {Date} date The selected month
37333              */
37334         'monthchange': true,
37335         /**
37336              * @event evententer
37337              * Fires when mouse over an event
37338              * @param {Calendar} this
37339              * @param {event} Event
37340              */
37341         'evententer': true,
37342         /**
37343              * @event eventleave
37344              * Fires when the mouse leaves an
37345              * @param {Calendar} this
37346              * @param {event}
37347              */
37348         'eventleave': true,
37349         /**
37350              * @event eventclick
37351              * Fires when the mouse click an
37352              * @param {Calendar} this
37353              * @param {event}
37354              */
37355         'eventclick': true,
37356         /**
37357              * @event eventrender
37358              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
37359              * @param {Calendar} this
37360              * @param {data} data to be modified
37361              */
37362         'eventrender': true
37363         
37364     });
37365
37366     Roo.grid.Grid.superclass.constructor.call(this);
37367     this.on('render', function() {
37368         this.view.el.addClass('x-grid-cal'); 
37369         
37370         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
37371
37372     },this);
37373     
37374     if (!Roo.grid.Calendar.style) {
37375         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
37376             
37377             
37378             '.x-grid-cal .x-grid-col' :  {
37379                 height: 'auto !important',
37380                 'vertical-align': 'top'
37381             },
37382             '.x-grid-cal  .fc-event-hori' : {
37383                 height: '14px'
37384             }
37385              
37386             
37387         }, Roo.id());
37388     }
37389
37390     
37391     
37392 };
37393 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
37394     /**
37395      * @cfg {Store} eventStore The store that loads events.
37396      */
37397     eventStore : 25,
37398
37399      
37400     activeDate : false,
37401     startDay : 0,
37402     autoWidth : true,
37403     monitorWindowResize : false,
37404
37405     
37406     resizeColumns : function() {
37407         var col = (this.view.el.getWidth() / 7) - 3;
37408         // loop through cols, and setWidth
37409         for(var i =0 ; i < 7 ; i++){
37410             this.cm.setColumnWidth(i, col);
37411         }
37412     },
37413      setDate :function(date) {
37414         
37415         Roo.log('setDate?');
37416         
37417         this.resizeColumns();
37418         var vd = this.activeDate;
37419         this.activeDate = date;
37420 //        if(vd && this.el){
37421 //            var t = date.getTime();
37422 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
37423 //                Roo.log('using add remove');
37424 //                
37425 //                this.fireEvent('monthchange', this, date);
37426 //                
37427 //                this.cells.removeClass("fc-state-highlight");
37428 //                this.cells.each(function(c){
37429 //                   if(c.dateValue == t){
37430 //                       c.addClass("fc-state-highlight");
37431 //                       setTimeout(function(){
37432 //                            try{c.dom.firstChild.focus();}catch(e){}
37433 //                       }, 50);
37434 //                       return false;
37435 //                   }
37436 //                   return true;
37437 //                });
37438 //                return;
37439 //            }
37440 //        }
37441         
37442         var days = date.getDaysInMonth();
37443         
37444         var firstOfMonth = date.getFirstDateOfMonth();
37445         var startingPos = firstOfMonth.getDay()-this.startDay;
37446         
37447         if(startingPos < this.startDay){
37448             startingPos += 7;
37449         }
37450         
37451         var pm = date.add(Date.MONTH, -1);
37452         var prevStart = pm.getDaysInMonth()-startingPos;
37453 //        
37454         
37455         
37456         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37457         
37458         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
37459         //this.cells.addClassOnOver('fc-state-hover');
37460         
37461         var cells = this.cells.elements;
37462         var textEls = this.textNodes;
37463         
37464         //Roo.each(cells, function(cell){
37465         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
37466         //});
37467         
37468         days += startingPos;
37469
37470         // convert everything to numbers so it's fast
37471         var day = 86400000;
37472         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
37473         //Roo.log(d);
37474         //Roo.log(pm);
37475         //Roo.log(prevStart);
37476         
37477         var today = new Date().clearTime().getTime();
37478         var sel = date.clearTime().getTime();
37479         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
37480         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
37481         var ddMatch = this.disabledDatesRE;
37482         var ddText = this.disabledDatesText;
37483         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
37484         var ddaysText = this.disabledDaysText;
37485         var format = this.format;
37486         
37487         var setCellClass = function(cal, cell){
37488             
37489             //Roo.log('set Cell Class');
37490             cell.title = "";
37491             var t = d.getTime();
37492             
37493             //Roo.log(d);
37494             
37495             
37496             cell.dateValue = t;
37497             if(t == today){
37498                 cell.className += " fc-today";
37499                 cell.className += " fc-state-highlight";
37500                 cell.title = cal.todayText;
37501             }
37502             if(t == sel){
37503                 // disable highlight in other month..
37504                 cell.className += " fc-state-highlight";
37505                 
37506             }
37507             // disabling
37508             if(t < min) {
37509                 //cell.className = " fc-state-disabled";
37510                 cell.title = cal.minText;
37511                 return;
37512             }
37513             if(t > max) {
37514                 //cell.className = " fc-state-disabled";
37515                 cell.title = cal.maxText;
37516                 return;
37517             }
37518             if(ddays){
37519                 if(ddays.indexOf(d.getDay()) != -1){
37520                     // cell.title = ddaysText;
37521                    // cell.className = " fc-state-disabled";
37522                 }
37523             }
37524             if(ddMatch && format){
37525                 var fvalue = d.dateFormat(format);
37526                 if(ddMatch.test(fvalue)){
37527                     cell.title = ddText.replace("%0", fvalue);
37528                    cell.className = " fc-state-disabled";
37529                 }
37530             }
37531             
37532             if (!cell.initialClassName) {
37533                 cell.initialClassName = cell.dom.className;
37534             }
37535             
37536             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
37537         };
37538
37539         var i = 0;
37540         
37541         for(; i < startingPos; i++) {
37542             cells[i].dayName =  (++prevStart);
37543             Roo.log(textEls[i]);
37544             d.setDate(d.getDate()+1);
37545             
37546             //cells[i].className = "fc-past fc-other-month";
37547             setCellClass(this, cells[i]);
37548         }
37549         
37550         var intDay = 0;
37551         
37552         for(; i < days; i++){
37553             intDay = i - startingPos + 1;
37554             cells[i].dayName =  (intDay);
37555             d.setDate(d.getDate()+1);
37556             
37557             cells[i].className = ''; // "x-date-active";
37558             setCellClass(this, cells[i]);
37559         }
37560         var extraDays = 0;
37561         
37562         for(; i < 42; i++) {
37563             //textEls[i].innerHTML = (++extraDays);
37564             
37565             d.setDate(d.getDate()+1);
37566             cells[i].dayName = (++extraDays);
37567             cells[i].className = "fc-future fc-other-month";
37568             setCellClass(this, cells[i]);
37569         }
37570         
37571         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
37572         
37573         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
37574         
37575         // this will cause all the cells to mis
37576         var rows= [];
37577         var i =0;
37578         for (var r = 0;r < 6;r++) {
37579             for (var c =0;c < 7;c++) {
37580                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
37581             }    
37582         }
37583         
37584         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
37585         for(i=0;i<cells.length;i++) {
37586             
37587             this.cells.elements[i].dayName = cells[i].dayName ;
37588             this.cells.elements[i].className = cells[i].className;
37589             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
37590             this.cells.elements[i].title = cells[i].title ;
37591             this.cells.elements[i].dateValue = cells[i].dateValue ;
37592         }
37593         
37594         
37595         
37596         
37597         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
37598         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
37599         
37600         ////if(totalRows != 6){
37601             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
37602            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
37603        // }
37604         
37605         this.fireEvent('monthchange', this, date);
37606         
37607         
37608     },
37609  /**
37610      * Returns the grid's SelectionModel.
37611      * @return {SelectionModel}
37612      */
37613     getSelectionModel : function(){
37614         if(!this.selModel){
37615             this.selModel = new Roo.grid.CellSelectionModel();
37616         }
37617         return this.selModel;
37618     },
37619
37620     load: function() {
37621         this.eventStore.load()
37622         
37623         
37624         
37625     },
37626     
37627     findCell : function(dt) {
37628         dt = dt.clearTime().getTime();
37629         var ret = false;
37630         this.cells.each(function(c){
37631             //Roo.log("check " +c.dateValue + '?=' + dt);
37632             if(c.dateValue == dt){
37633                 ret = c;
37634                 return false;
37635             }
37636             return true;
37637         });
37638         
37639         return ret;
37640     },
37641     
37642     findCells : function(rec) {
37643         var s = rec.data.start_dt.clone().clearTime().getTime();
37644        // Roo.log(s);
37645         var e= rec.data.end_dt.clone().clearTime().getTime();
37646        // Roo.log(e);
37647         var ret = [];
37648         this.cells.each(function(c){
37649              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
37650             
37651             if(c.dateValue > e){
37652                 return ;
37653             }
37654             if(c.dateValue < s){
37655                 return ;
37656             }
37657             ret.push(c);
37658         });
37659         
37660         return ret;    
37661     },
37662     
37663     findBestRow: function(cells)
37664     {
37665         var ret = 0;
37666         
37667         for (var i =0 ; i < cells.length;i++) {
37668             ret  = Math.max(cells[i].rows || 0,ret);
37669         }
37670         return ret;
37671         
37672     },
37673     
37674     
37675     addItem : function(rec)
37676     {
37677         // look for vertical location slot in
37678         var cells = this.findCells(rec);
37679         
37680         rec.row = this.findBestRow(cells);
37681         
37682         // work out the location.
37683         
37684         var crow = false;
37685         var rows = [];
37686         for(var i =0; i < cells.length; i++) {
37687             if (!crow) {
37688                 crow = {
37689                     start : cells[i],
37690                     end :  cells[i]
37691                 };
37692                 continue;
37693             }
37694             if (crow.start.getY() == cells[i].getY()) {
37695                 // on same row.
37696                 crow.end = cells[i];
37697                 continue;
37698             }
37699             // different row.
37700             rows.push(crow);
37701             crow = {
37702                 start: cells[i],
37703                 end : cells[i]
37704             };
37705             
37706         }
37707         
37708         rows.push(crow);
37709         rec.els = [];
37710         rec.rows = rows;
37711         rec.cells = cells;
37712         for (var i = 0; i < cells.length;i++) {
37713             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
37714             
37715         }
37716         
37717         
37718     },
37719     
37720     clearEvents: function() {
37721         
37722         if (!this.eventStore.getCount()) {
37723             return;
37724         }
37725         // reset number of rows in cells.
37726         Roo.each(this.cells.elements, function(c){
37727             c.rows = 0;
37728         });
37729         
37730         this.eventStore.each(function(e) {
37731             this.clearEvent(e);
37732         },this);
37733         
37734     },
37735     
37736     clearEvent : function(ev)
37737     {
37738         if (ev.els) {
37739             Roo.each(ev.els, function(el) {
37740                 el.un('mouseenter' ,this.onEventEnter, this);
37741                 el.un('mouseleave' ,this.onEventLeave, this);
37742                 el.remove();
37743             },this);
37744             ev.els = [];
37745         }
37746     },
37747     
37748     
37749     renderEvent : function(ev,ctr) {
37750         if (!ctr) {
37751              ctr = this.view.el.select('.fc-event-container',true).first();
37752         }
37753         
37754          
37755         this.clearEvent(ev);
37756             //code
37757        
37758         
37759         
37760         ev.els = [];
37761         var cells = ev.cells;
37762         var rows = ev.rows;
37763         this.fireEvent('eventrender', this, ev);
37764         
37765         for(var i =0; i < rows.length; i++) {
37766             
37767             cls = '';
37768             if (i == 0) {
37769                 cls += ' fc-event-start';
37770             }
37771             if ((i+1) == rows.length) {
37772                 cls += ' fc-event-end';
37773             }
37774             
37775             //Roo.log(ev.data);
37776             // how many rows should it span..
37777             var cg = this.eventTmpl.append(ctr,Roo.apply({
37778                 fccls : cls
37779                 
37780             }, ev.data) , true);
37781             
37782             
37783             cg.on('mouseenter' ,this.onEventEnter, this, ev);
37784             cg.on('mouseleave' ,this.onEventLeave, this, ev);
37785             cg.on('click', this.onEventClick, this, ev);
37786             
37787             ev.els.push(cg);
37788             
37789             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
37790             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
37791             //Roo.log(cg);
37792              
37793             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
37794             cg.setWidth(ebox.right - sbox.x -2);
37795         }
37796     },
37797     
37798     renderEvents: function()
37799     {   
37800         // first make sure there is enough space..
37801         
37802         if (!this.eventTmpl) {
37803             this.eventTmpl = new Roo.Template(
37804                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
37805                     '<div class="fc-event-inner">' +
37806                         '<span class="fc-event-time">{time}</span>' +
37807                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
37808                     '</div>' +
37809                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
37810                 '</div>'
37811             );
37812                 
37813         }
37814                
37815         
37816         
37817         this.cells.each(function(c) {
37818             //Roo.log(c.select('.fc-day-content div',true).first());
37819             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
37820         });
37821         
37822         var ctr = this.view.el.select('.fc-event-container',true).first();
37823         
37824         var cls;
37825         this.eventStore.each(function(ev){
37826             
37827             this.renderEvent(ev);
37828              
37829              
37830         }, this);
37831         this.view.layout();
37832         
37833     },
37834     
37835     onEventEnter: function (e, el,event,d) {
37836         this.fireEvent('evententer', this, el, event);
37837     },
37838     
37839     onEventLeave: function (e, el,event,d) {
37840         this.fireEvent('eventleave', this, el, event);
37841     },
37842     
37843     onEventClick: function (e, el,event,d) {
37844         this.fireEvent('eventclick', this, el, event);
37845     },
37846     
37847     onMonthChange: function () {
37848         this.store.load();
37849     },
37850     
37851     onLoad: function () {
37852         
37853         //Roo.log('calendar onload');
37854 //         
37855         if(this.eventStore.getCount() > 0){
37856             
37857            
37858             
37859             this.eventStore.each(function(d){
37860                 
37861                 
37862                 // FIXME..
37863                 var add =   d.data;
37864                 if (typeof(add.end_dt) == 'undefined')  {
37865                     Roo.log("Missing End time in calendar data: ");
37866                     Roo.log(d);
37867                     return;
37868                 }
37869                 if (typeof(add.start_dt) == 'undefined')  {
37870                     Roo.log("Missing Start time in calendar data: ");
37871                     Roo.log(d);
37872                     return;
37873                 }
37874                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
37875                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
37876                 add.id = add.id || d.id;
37877                 add.title = add.title || '??';
37878                 
37879                 this.addItem(d);
37880                 
37881              
37882             },this);
37883         }
37884         
37885         this.renderEvents();
37886     }
37887     
37888
37889 });
37890 /*
37891  grid : {
37892                 xtype: 'Grid',
37893                 xns: Roo.grid,
37894                 listeners : {
37895                     render : function ()
37896                     {
37897                         _this.grid = this;
37898                         
37899                         if (!this.view.el.hasClass('course-timesheet')) {
37900                             this.view.el.addClass('course-timesheet');
37901                         }
37902                         if (this.tsStyle) {
37903                             this.ds.load({});
37904                             return; 
37905                         }
37906                         Roo.log('width');
37907                         Roo.log(_this.grid.view.el.getWidth());
37908                         
37909                         
37910                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
37911                             '.course-timesheet .x-grid-row' : {
37912                                 height: '80px'
37913                             },
37914                             '.x-grid-row td' : {
37915                                 'vertical-align' : 0
37916                             },
37917                             '.course-edit-link' : {
37918                                 'color' : 'blue',
37919                                 'text-overflow' : 'ellipsis',
37920                                 'overflow' : 'hidden',
37921                                 'white-space' : 'nowrap',
37922                                 'cursor' : 'pointer'
37923                             },
37924                             '.sub-link' : {
37925                                 'color' : 'green'
37926                             },
37927                             '.de-act-sup-link' : {
37928                                 'color' : 'purple',
37929                                 'text-decoration' : 'line-through'
37930                             },
37931                             '.de-act-link' : {
37932                                 'color' : 'red',
37933                                 'text-decoration' : 'line-through'
37934                             },
37935                             '.course-timesheet .course-highlight' : {
37936                                 'border-top-style': 'dashed !important',
37937                                 'border-bottom-bottom': 'dashed !important'
37938                             },
37939                             '.course-timesheet .course-item' : {
37940                                 'font-family'   : 'tahoma, arial, helvetica',
37941                                 'font-size'     : '11px',
37942                                 'overflow'      : 'hidden',
37943                                 'padding-left'  : '10px',
37944                                 'padding-right' : '10px',
37945                                 'padding-top' : '10px' 
37946                             }
37947                             
37948                         }, Roo.id());
37949                                 this.ds.load({});
37950                     }
37951                 },
37952                 autoWidth : true,
37953                 monitorWindowResize : false,
37954                 cellrenderer : function(v,x,r)
37955                 {
37956                     return v;
37957                 },
37958                 sm : {
37959                     xtype: 'CellSelectionModel',
37960                     xns: Roo.grid
37961                 },
37962                 dataSource : {
37963                     xtype: 'Store',
37964                     xns: Roo.data,
37965                     listeners : {
37966                         beforeload : function (_self, options)
37967                         {
37968                             options.params = options.params || {};
37969                             options.params._month = _this.monthField.getValue();
37970                             options.params.limit = 9999;
37971                             options.params['sort'] = 'when_dt';    
37972                             options.params['dir'] = 'ASC';    
37973                             this.proxy.loadResponse = this.loadResponse;
37974                             Roo.log("load?");
37975                             //this.addColumns();
37976                         },
37977                         load : function (_self, records, options)
37978                         {
37979                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
37980                                 // if you click on the translation.. you can edit it...
37981                                 var el = Roo.get(this);
37982                                 var id = el.dom.getAttribute('data-id');
37983                                 var d = el.dom.getAttribute('data-date');
37984                                 var t = el.dom.getAttribute('data-time');
37985                                 //var id = this.child('span').dom.textContent;
37986                                 
37987                                 //Roo.log(this);
37988                                 Pman.Dialog.CourseCalendar.show({
37989                                     id : id,
37990                                     when_d : d,
37991                                     when_t : t,
37992                                     productitem_active : id ? 1 : 0
37993                                 }, function() {
37994                                     _this.grid.ds.load({});
37995                                 });
37996                            
37997                            });
37998                            
37999                            _this.panel.fireEvent('resize', [ '', '' ]);
38000                         }
38001                     },
38002                     loadResponse : function(o, success, response){
38003                             // this is overridden on before load..
38004                             
38005                             Roo.log("our code?");       
38006                             //Roo.log(success);
38007                             //Roo.log(response)
38008                             delete this.activeRequest;
38009                             if(!success){
38010                                 this.fireEvent("loadexception", this, o, response);
38011                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38012                                 return;
38013                             }
38014                             var result;
38015                             try {
38016                                 result = o.reader.read(response);
38017                             }catch(e){
38018                                 Roo.log("load exception?");
38019                                 this.fireEvent("loadexception", this, o, response, e);
38020                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
38021                                 return;
38022                             }
38023                             Roo.log("ready...");        
38024                             // loop through result.records;
38025                             // and set this.tdate[date] = [] << array of records..
38026                             _this.tdata  = {};
38027                             Roo.each(result.records, function(r){
38028                                 //Roo.log(r.data);
38029                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
38030                                     _this.tdata[r.data.when_dt.format('j')] = [];
38031                                 }
38032                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
38033                             });
38034                             
38035                             //Roo.log(_this.tdata);
38036                             
38037                             result.records = [];
38038                             result.totalRecords = 6;
38039                     
38040                             // let's generate some duumy records for the rows.
38041                             //var st = _this.dateField.getValue();
38042                             
38043                             // work out monday..
38044                             //st = st.add(Date.DAY, -1 * st.format('w'));
38045                             
38046                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38047                             
38048                             var firstOfMonth = date.getFirstDayOfMonth();
38049                             var days = date.getDaysInMonth();
38050                             var d = 1;
38051                             var firstAdded = false;
38052                             for (var i = 0; i < result.totalRecords ; i++) {
38053                                 //var d= st.add(Date.DAY, i);
38054                                 var row = {};
38055                                 var added = 0;
38056                                 for(var w = 0 ; w < 7 ; w++){
38057                                     if(!firstAdded && firstOfMonth != w){
38058                                         continue;
38059                                     }
38060                                     if(d > days){
38061                                         continue;
38062                                     }
38063                                     firstAdded = true;
38064                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
38065                                     row['weekday'+w] = String.format(
38066                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
38067                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
38068                                                     d,
38069                                                     date.format('Y-m-')+dd
38070                                                 );
38071                                     added++;
38072                                     if(typeof(_this.tdata[d]) != 'undefined'){
38073                                         Roo.each(_this.tdata[d], function(r){
38074                                             var is_sub = '';
38075                                             var deactive = '';
38076                                             var id = r.id;
38077                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
38078                                             if(r.parent_id*1>0){
38079                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
38080                                                 id = r.parent_id;
38081                                             }
38082                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
38083                                                 deactive = 'de-act-link';
38084                                             }
38085                                             
38086                                             row['weekday'+w] += String.format(
38087                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
38088                                                     id, //0
38089                                                     r.product_id_name, //1
38090                                                     r.when_dt.format('h:ia'), //2
38091                                                     is_sub, //3
38092                                                     deactive, //4
38093                                                     desc // 5
38094                                             );
38095                                         });
38096                                     }
38097                                     d++;
38098                                 }
38099                                 
38100                                 // only do this if something added..
38101                                 if(added > 0){ 
38102                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
38103                                 }
38104                                 
38105                                 
38106                                 // push it twice. (second one with an hour..
38107                                 
38108                             }
38109                             //Roo.log(result);
38110                             this.fireEvent("load", this, o, o.request.arg);
38111                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
38112                         },
38113                     sortInfo : {field: 'when_dt', direction : 'ASC' },
38114                     proxy : {
38115                         xtype: 'HttpProxy',
38116                         xns: Roo.data,
38117                         method : 'GET',
38118                         url : baseURL + '/Roo/Shop_course.php'
38119                     },
38120                     reader : {
38121                         xtype: 'JsonReader',
38122                         xns: Roo.data,
38123                         id : 'id',
38124                         fields : [
38125                             {
38126                                 'name': 'id',
38127                                 'type': 'int'
38128                             },
38129                             {
38130                                 'name': 'when_dt',
38131                                 'type': 'string'
38132                             },
38133                             {
38134                                 'name': 'end_dt',
38135                                 'type': 'string'
38136                             },
38137                             {
38138                                 'name': 'parent_id',
38139                                 'type': 'int'
38140                             },
38141                             {
38142                                 'name': 'product_id',
38143                                 'type': 'int'
38144                             },
38145                             {
38146                                 'name': 'productitem_id',
38147                                 'type': 'int'
38148                             },
38149                             {
38150                                 'name': 'guid',
38151                                 'type': 'int'
38152                             }
38153                         ]
38154                     }
38155                 },
38156                 toolbar : {
38157                     xtype: 'Toolbar',
38158                     xns: Roo,
38159                     items : [
38160                         {
38161                             xtype: 'Button',
38162                             xns: Roo.Toolbar,
38163                             listeners : {
38164                                 click : function (_self, e)
38165                                 {
38166                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38167                                     sd.setMonth(sd.getMonth()-1);
38168                                     _this.monthField.setValue(sd.format('Y-m-d'));
38169                                     _this.grid.ds.load({});
38170                                 }
38171                             },
38172                             text : "Back"
38173                         },
38174                         {
38175                             xtype: 'Separator',
38176                             xns: Roo.Toolbar
38177                         },
38178                         {
38179                             xtype: 'MonthField',
38180                             xns: Roo.form,
38181                             listeners : {
38182                                 render : function (_self)
38183                                 {
38184                                     _this.monthField = _self;
38185                                    // _this.monthField.set  today
38186                                 },
38187                                 select : function (combo, date)
38188                                 {
38189                                     _this.grid.ds.load({});
38190                                 }
38191                             },
38192                             value : (function() { return new Date(); })()
38193                         },
38194                         {
38195                             xtype: 'Separator',
38196                             xns: Roo.Toolbar
38197                         },
38198                         {
38199                             xtype: 'TextItem',
38200                             xns: Roo.Toolbar,
38201                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
38202                         },
38203                         {
38204                             xtype: 'Fill',
38205                             xns: Roo.Toolbar
38206                         },
38207                         {
38208                             xtype: 'Button',
38209                             xns: Roo.Toolbar,
38210                             listeners : {
38211                                 click : function (_self, e)
38212                                 {
38213                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
38214                                     sd.setMonth(sd.getMonth()+1);
38215                                     _this.monthField.setValue(sd.format('Y-m-d'));
38216                                     _this.grid.ds.load({});
38217                                 }
38218                             },
38219                             text : "Next"
38220                         }
38221                     ]
38222                 },
38223                  
38224             }
38225         };
38226         
38227         *//*
38228  * Based on:
38229  * Ext JS Library 1.1.1
38230  * Copyright(c) 2006-2007, Ext JS, LLC.
38231  *
38232  * Originally Released Under LGPL - original licence link has changed is not relivant.
38233  *
38234  * Fork - LGPL
38235  * <script type="text/javascript">
38236  */
38237  
38238 /**
38239  * @class Roo.LoadMask
38240  * A simple utility class for generically masking elements while loading data.  If the element being masked has
38241  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
38242  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
38243  * element's UpdateManager load indicator and will be destroyed after the initial load.
38244  * @constructor
38245  * Create a new LoadMask
38246  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
38247  * @param {Object} config The config object
38248  */
38249 Roo.LoadMask = function(el, config){
38250     this.el = Roo.get(el);
38251     Roo.apply(this, config);
38252     if(this.store){
38253         this.store.on('beforeload', this.onBeforeLoad, this);
38254         this.store.on('load', this.onLoad, this);
38255         this.store.on('loadexception', this.onLoadException, this);
38256         this.removeMask = false;
38257     }else{
38258         var um = this.el.getUpdateManager();
38259         um.showLoadIndicator = false; // disable the default indicator
38260         um.on('beforeupdate', this.onBeforeLoad, this);
38261         um.on('update', this.onLoad, this);
38262         um.on('failure', this.onLoad, this);
38263         this.removeMask = true;
38264     }
38265 };
38266
38267 Roo.LoadMask.prototype = {
38268     /**
38269      * @cfg {Boolean} removeMask
38270      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
38271      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
38272      */
38273     /**
38274      * @cfg {String} msg
38275      * The text to display in a centered loading message box (defaults to 'Loading...')
38276      */
38277     msg : 'Loading...',
38278     /**
38279      * @cfg {String} msgCls
38280      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
38281      */
38282     msgCls : 'x-mask-loading',
38283
38284     /**
38285      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
38286      * @type Boolean
38287      */
38288     disabled: false,
38289
38290     /**
38291      * Disables the mask to prevent it from being displayed
38292      */
38293     disable : function(){
38294        this.disabled = true;
38295     },
38296
38297     /**
38298      * Enables the mask so that it can be displayed
38299      */
38300     enable : function(){
38301         this.disabled = false;
38302     },
38303     
38304     onLoadException : function()
38305     {
38306         Roo.log(arguments);
38307         
38308         if (typeof(arguments[3]) != 'undefined') {
38309             Roo.MessageBox.alert("Error loading",arguments[3]);
38310         } 
38311         /*
38312         try {
38313             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38314                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38315             }   
38316         } catch(e) {
38317             
38318         }
38319         */
38320     
38321         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38322     },
38323     // private
38324     onLoad : function()
38325     {
38326         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
38327     },
38328
38329     // private
38330     onBeforeLoad : function(){
38331         if(!this.disabled){
38332             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
38333         }
38334     },
38335
38336     // private
38337     destroy : function(){
38338         if(this.store){
38339             this.store.un('beforeload', this.onBeforeLoad, this);
38340             this.store.un('load', this.onLoad, this);
38341             this.store.un('loadexception', this.onLoadException, this);
38342         }else{
38343             var um = this.el.getUpdateManager();
38344             um.un('beforeupdate', this.onBeforeLoad, this);
38345             um.un('update', this.onLoad, this);
38346             um.un('failure', this.onLoad, this);
38347         }
38348     }
38349 };/*
38350  * Based on:
38351  * Ext JS Library 1.1.1
38352  * Copyright(c) 2006-2007, Ext JS, LLC.
38353  *
38354  * Originally Released Under LGPL - original licence link has changed is not relivant.
38355  *
38356  * Fork - LGPL
38357  * <script type="text/javascript">
38358  */
38359
38360
38361 /**
38362  * @class Roo.XTemplate
38363  * @extends Roo.Template
38364  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
38365 <pre><code>
38366 var t = new Roo.XTemplate(
38367         '&lt;select name="{name}"&gt;',
38368                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
38369         '&lt;/select&gt;'
38370 );
38371  
38372 // then append, applying the master template values
38373  </code></pre>
38374  *
38375  * Supported features:
38376  *
38377  *  Tags:
38378
38379 <pre><code>
38380       {a_variable} - output encoded.
38381       {a_variable.format:("Y-m-d")} - call a method on the variable
38382       {a_variable:raw} - unencoded output
38383       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
38384       {a_variable:this.method_on_template(...)} - call a method on the template object.
38385  
38386 </code></pre>
38387  *  The tpl tag:
38388 <pre><code>
38389         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
38390         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
38391         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
38392         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
38393   
38394         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
38395         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
38396 </code></pre>
38397  *      
38398  */
38399 Roo.XTemplate = function()
38400 {
38401     Roo.XTemplate.superclass.constructor.apply(this, arguments);
38402     if (this.html) {
38403         this.compile();
38404     }
38405 };
38406
38407
38408 Roo.extend(Roo.XTemplate, Roo.Template, {
38409
38410     /**
38411      * The various sub templates
38412      */
38413     tpls : false,
38414     /**
38415      *
38416      * basic tag replacing syntax
38417      * WORD:WORD()
38418      *
38419      * // you can fake an object call by doing this
38420      *  x.t:(test,tesT) 
38421      * 
38422      */
38423     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
38424
38425     /**
38426      * compile the template
38427      *
38428      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
38429      *
38430      */
38431     compile: function()
38432     {
38433         var s = this.html;
38434      
38435         s = ['<tpl>', s, '</tpl>'].join('');
38436     
38437         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
38438             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
38439             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
38440             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
38441             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
38442             m,
38443             id     = 0,
38444             tpls   = [];
38445     
38446         while(true == !!(m = s.match(re))){
38447             var forMatch   = m[0].match(nameRe),
38448                 ifMatch   = m[0].match(ifRe),
38449                 execMatch   = m[0].match(execRe),
38450                 namedMatch   = m[0].match(namedRe),
38451                 
38452                 exp  = null, 
38453                 fn   = null,
38454                 exec = null,
38455                 name = forMatch && forMatch[1] ? forMatch[1] : '';
38456                 
38457             if (ifMatch) {
38458                 // if - puts fn into test..
38459                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
38460                 if(exp){
38461                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
38462                 }
38463             }
38464             
38465             if (execMatch) {
38466                 // exec - calls a function... returns empty if true is  returned.
38467                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
38468                 if(exp){
38469                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
38470                 }
38471             }
38472             
38473             
38474             if (name) {
38475                 // for = 
38476                 switch(name){
38477                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
38478                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
38479                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
38480                 }
38481             }
38482             var uid = namedMatch ? namedMatch[1] : id;
38483             
38484             
38485             tpls.push({
38486                 id:     namedMatch ? namedMatch[1] : id,
38487                 target: name,
38488                 exec:   exec,
38489                 test:   fn,
38490                 body:   m[1] || ''
38491             });
38492             if (namedMatch) {
38493                 s = s.replace(m[0], '');
38494             } else { 
38495                 s = s.replace(m[0], '{xtpl'+ id + '}');
38496             }
38497             ++id;
38498         }
38499         this.tpls = [];
38500         for(var i = tpls.length-1; i >= 0; --i){
38501             this.compileTpl(tpls[i]);
38502             this.tpls[tpls[i].id] = tpls[i];
38503         }
38504         this.master = tpls[tpls.length-1];
38505         return this;
38506     },
38507     /**
38508      * same as applyTemplate, except it's done to one of the subTemplates
38509      * when using named templates, you can do:
38510      *
38511      * var str = pl.applySubTemplate('your-name', values);
38512      *
38513      * 
38514      * @param {Number} id of the template
38515      * @param {Object} values to apply to template
38516      * @param {Object} parent (normaly the instance of this object)
38517      */
38518     applySubTemplate : function(id, values, parent)
38519     {
38520         
38521         
38522         var t = this.tpls[id];
38523         
38524         
38525         try { 
38526             if(t.test && !t.test.call(this, values, parent)){
38527                 return '';
38528             }
38529         } catch(e) {
38530             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
38531             Roo.log(e.toString());
38532             Roo.log(t.test);
38533             return ''
38534         }
38535         try { 
38536             
38537             if(t.exec && t.exec.call(this, values, parent)){
38538                 return '';
38539             }
38540         } catch(e) {
38541             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
38542             Roo.log(e.toString());
38543             Roo.log(t.exec);
38544             return ''
38545         }
38546         try {
38547             var vs = t.target ? t.target.call(this, values, parent) : values;
38548             parent = t.target ? values : parent;
38549             if(t.target && vs instanceof Array){
38550                 var buf = [];
38551                 for(var i = 0, len = vs.length; i < len; i++){
38552                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
38553                 }
38554                 return buf.join('');
38555             }
38556             return t.compiled.call(this, vs, parent);
38557         } catch (e) {
38558             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
38559             Roo.log(e.toString());
38560             Roo.log(t.compiled);
38561             return '';
38562         }
38563     },
38564
38565     compileTpl : function(tpl)
38566     {
38567         var fm = Roo.util.Format;
38568         var useF = this.disableFormats !== true;
38569         var sep = Roo.isGecko ? "+" : ",";
38570         var undef = function(str) {
38571             Roo.log("Property not found :"  + str);
38572             return '';
38573         };
38574         
38575         var fn = function(m, name, format, args)
38576         {
38577             //Roo.log(arguments);
38578             args = args ? args.replace(/\\'/g,"'") : args;
38579             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
38580             if (typeof(format) == 'undefined') {
38581                 format= 'htmlEncode';
38582             }
38583             if (format == 'raw' ) {
38584                 format = false;
38585             }
38586             
38587             if(name.substr(0, 4) == 'xtpl'){
38588                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
38589             }
38590             
38591             // build an array of options to determine if value is undefined..
38592             
38593             // basically get 'xxxx.yyyy' then do
38594             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
38595             //    (function () { Roo.log("Property not found"); return ''; })() :
38596             //    ......
38597             
38598             var udef_ar = [];
38599             var lookfor = '';
38600             Roo.each(name.split('.'), function(st) {
38601                 lookfor += (lookfor.length ? '.': '') + st;
38602                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
38603             });
38604             
38605             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
38606             
38607             
38608             if(format && useF){
38609                 
38610                 args = args ? ',' + args : "";
38611                  
38612                 if(format.substr(0, 5) != "this."){
38613                     format = "fm." + format + '(';
38614                 }else{
38615                     format = 'this.call("'+ format.substr(5) + '", ';
38616                     args = ", values";
38617                 }
38618                 
38619                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
38620             }
38621              
38622             if (args.length) {
38623                 // called with xxyx.yuu:(test,test)
38624                 // change to ()
38625                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
38626             }
38627             // raw.. - :raw modifier..
38628             return "'"+ sep + udef_st  + name + ")"+sep+"'";
38629             
38630         };
38631         var body;
38632         // branched to use + in gecko and [].join() in others
38633         if(Roo.isGecko){
38634             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
38635                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
38636                     "';};};";
38637         }else{
38638             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
38639             body.push(tpl.body.replace(/(\r\n|\n)/g,
38640                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
38641             body.push("'].join('');};};");
38642             body = body.join('');
38643         }
38644         
38645         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
38646        
38647         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
38648         eval(body);
38649         
38650         return this;
38651     },
38652
38653     applyTemplate : function(values){
38654         return this.master.compiled.call(this, values, {});
38655         //var s = this.subs;
38656     },
38657
38658     apply : function(){
38659         return this.applyTemplate.apply(this, arguments);
38660     }
38661
38662  });
38663
38664 Roo.XTemplate.from = function(el){
38665     el = Roo.getDom(el);
38666     return new Roo.XTemplate(el.value || el.innerHTML);
38667 };